src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeStore.java

Print this page




  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.runtime;
  27 
  28 import java.io.BufferedInputStream;
  29 import java.io.BufferedOutputStream;
  30 import java.io.File;
  31 import java.io.FileInputStream;
  32 import java.io.FileOutputStream;
  33 import java.io.IOException;
  34 import java.io.ObjectInputStream;
  35 import java.io.ObjectOutputStream;
  36 import java.io.Serializable;

  37 import java.security.AccessController;
  38 import java.security.PrivilegedActionException;
  39 import java.security.PrivilegedExceptionAction;

  40 import java.util.Map;

  41 import jdk.nashorn.internal.codegen.types.Type;
  42 import jdk.nashorn.internal.runtime.logging.DebugLogger;
  43 import jdk.nashorn.internal.runtime.logging.Loggable;
  44 import jdk.nashorn.internal.runtime.logging.Logger;

  45 
  46 /**
  47  * A code cache for persistent caching of compiled scripts.
  48  */
  49 @Logger(name="codestore")
  50 final class CodeStore implements Loggable {
  51 
  52     private final File dir;
  53     private final int minSize;
  54     private final DebugLogger log;
  55 
  56     // Default minimum size for storing a compiled script class
  57     private final static int DEFAULT_MIN_SIZE = 1000;
  58 
  59     /**
  60      * Constructor
  61      * @throws IOException
  62      */
  63     public CodeStore(final Context context, final String path) throws IOException {
  64         this(context, path, DEFAULT_MIN_SIZE);
  65     }
  66 
  67     /**
  68      * Constructor
  69      * @param path directory to store code in
  70      * @param minSize minimum file size for caching scripts
  71      * @throws IOException
  72      */
  73     public CodeStore(final Context context, final String path, final int minSize) throws IOException {
  74         this.dir = checkDirectory(path);
  75         this.minSize = minSize;
  76         this.log = initLogger(context);
  77     }
  78 
  79     @Override
  80     public DebugLogger initLogger(final Context context) {
  81          return context.getLogger(getClass());

  82     }
  83 
  84     @Override
  85     public DebugLogger getLogger() {
  86         return log;
  87     }
  88 
  89     private static File checkDirectory(final String path) throws IOException {








  90         try {
  91             return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() {
  92                 @Override
  93                 public File run() throws IOException {
  94                     final File dir = new File(path).getAbsoluteFile();
  95                     if (!dir.exists() && !dir.mkdirs()) {
  96                         throw new IOException("Could not create directory: " + dir.getPath());
  97                     } else if (!dir.isDirectory()) {
  98                         throw new IOException("Not a directory: " + dir.getPath());
  99                     } else if (!dir.canRead() || !dir.canWrite()) {
 100                         throw new IOException("Directory not readable or writable: " + dir.getPath());








 101                     }
 102                     return dir;




















 103                 }
 104             });
 105         } catch (final PrivilegedActionException e) {
 106             throw (IOException) e.getException();






































 107         }
 108     }
 109 
 110     private File getCacheFile(final Source source, final String functionKey) {
 111         return new File(dir, source.getDigest() + '-' + functionKey);
 112     }
 113 
 114     /**
 115      * Generate a string representing the function with {@code functionId} and {@code paramTypes}.
 116      * @param functionId function id
 117      * @param paramTypes parameter types
 118      * @return a string representing the function
 119      */
 120     public static String getCacheKey(final int functionId, final Type[] paramTypes) {
 121         final StringBuilder b = new StringBuilder().append(functionId);
 122         if(paramTypes != null && paramTypes.length > 0) {
 123             b.append('-');
 124             for(final Type t: paramTypes) {
 125                 b.append(Type.getShortSignatureDescriptor(t));
 126             }
 127         }
 128         return b.toString();
 129     }
 130 
 131     /**
 132      * Return a compiled script from the cache, or null if it isn't found.












 133      *
 134      * @param source the source
 135      * @param functionKey the function key
 136      * @return the stored script or null









 137      */
 138     public StoredScript loadScript(final Source source, final String functionKey) {


































 139         if (source.getLength() < minSize) {
 140             return null;
 141         }
 142 
 143         final File file = getCacheFile(source, functionKey);
 144 
 145         try {
 146             return AccessController.doPrivileged(new PrivilegedExceptionAction<StoredScript>() {
 147                 @Override
 148                 public StoredScript run() throws IOException, ClassNotFoundException {
 149                     if (!file.exists()) {
 150                         return null;
 151                     }
 152                     try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) {
 153                         final StoredScript storedScript = (StoredScript) in.readObject();
 154                         getLogger().info("loaded ", source, "-", functionKey);
 155                         return storedScript;
 156                     }
 157                 }
 158             });
 159         } catch (final PrivilegedActionException e) {
 160             getLogger().warning("failed to load ", source, "-", functionKey, ": ", e.getException());
 161             return null;
 162         }
 163     }
 164 
 165     /**
 166      * Store a compiled script in the cache.
 167      *
 168      * @param functionKey the function key
 169      * @param source the source
 170      * @param mainClassName the main class name
 171      * @param classBytes a map of class bytes
 172      * @param constants the constants array
 173      */
 174     public void storeScript(final String functionKey, final Source source, final String mainClassName, final Map<String, byte[]> classBytes,
 175                           final Map<Integer, FunctionInitializer> initializers, final Object[] constants, final int compilationId) {
 176         if (source.getLength() < minSize) {
 177             return;
 178         }
 179         for (final Object constant : constants) {
 180             // Make sure all constant data is serializable
 181             if (! (constant instanceof Serializable)) {
 182                 getLogger().warning("cannot store ", source, " non serializable constant ", constant);
 183                 return;
 184             }
 185         }
 186 
 187         final File file = getCacheFile(source, functionKey);
 188         final StoredScript script = new StoredScript(compilationId, mainClassName, classBytes, initializers, constants);
 189 
 190         try {
 191             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
 192                 @Override
 193                 public Void run() throws IOException {
 194                     try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
 195                         out.writeObject(script);
 196                     }
 197                     getLogger().info("stored ", source, "-", functionKey);
 198                     return null;
 199                 }
 200             });
 201         } catch (final PrivilegedActionException e) {
 202             getLogger().warning("failed to store ", script, "-", functionKey, ": ", e.getException());















 203         }
 204     }
 205 }
 206 


  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.runtime;
  27 
  28 import java.io.BufferedInputStream;
  29 import java.io.BufferedOutputStream;
  30 import java.io.File;
  31 import java.io.FileInputStream;
  32 import java.io.FileOutputStream;
  33 import java.io.IOException;
  34 import java.io.ObjectInputStream;
  35 import java.io.ObjectOutputStream;
  36 import java.io.Serializable;
  37 import java.security.AccessControlException;
  38 import java.security.AccessController;
  39 import java.security.PrivilegedActionException;
  40 import java.security.PrivilegedExceptionAction;
  41 import java.util.Iterator;
  42 import java.util.Map;
  43 import java.util.ServiceLoader;
  44 import jdk.nashorn.internal.codegen.types.Type;
  45 import jdk.nashorn.internal.runtime.logging.DebugLogger;
  46 import jdk.nashorn.internal.runtime.logging.Loggable;
  47 import jdk.nashorn.internal.runtime.logging.Logger;
  48 import jdk.nashorn.internal.runtime.options.Options;
  49 
  50 /**
  51  * A code cache for persistent caching of compiled scripts.
  52  */
  53 @Logger(name="codestore")
  54 public abstract class CodeStore implements Loggable {







  55 
  56     /**
  57      * Permission needed to provide a CodeStore instance via ServiceLoader.

  58      */
  59     public final static String NASHORN_PROVIDE_CODE_STORE = "nashorn.provideCodeStore";
  60 
  61     private DebugLogger log;
  62 
  63     /**
  64      * Constructor



  65      */
  66     protected CodeStore() {



  67     }
  68 
  69     @Override
  70     public DebugLogger initLogger(final Context context) {
  71         log = context.getLogger(getClass());
  72         return log;
  73     }
  74 
  75     @Override
  76     public DebugLogger getLogger() {
  77         return log;
  78     }
  79 
  80     /**
  81      * Returns a new code store instance.
  82      *
  83      * @param context the current context
  84      * @return The instance
  85      * @throws IOException If an error occurs
  86      */
  87     public static CodeStore newCodeStore(final Context context) throws IOException {
  88         final Class<CodeStore> baseClass = CodeStore.class;
  89         try {
  90             // security check first
  91             final SecurityManager sm = System.getSecurityManager();
  92             if (sm != null) {
  93                 sm.checkPermission(new RuntimePermission(NASHORN_PROVIDE_CODE_STORE));
  94             }
  95             final ServiceLoader<CodeStore> services = ServiceLoader.load(baseClass);
  96             final Iterator<CodeStore> iterator = services.iterator();
  97             if (iterator.hasNext()) {
  98                 final CodeStore store = iterator.next();
  99                 store.initLogger(context).info("using code store provider ", store.getClass().getCanonicalName());
 100                 return store;
 101             }
 102         } catch (final AccessControlException e) {
 103             context.getLogger(CodeStore.class).warning("failed to load code store provider ", e);
 104         }
 105         final CodeStore store = new DirectoryCodeStore();
 106         store.initLogger(context);
 107         return store;
 108     }
 109 
 110 
 111     /**
 112      * Store a compiled script in the cache.
 113      *
 114      * @param functionKey   the function key
 115      * @param source        the source
 116      * @param mainClassName the main class name
 117      * @param classBytes    a map of class bytes
 118      * @param initializers  the function initializers
 119      * @param constants     the constants array
 120      * @param compilationId the compilation id
 121      */
 122     public StoredScript store(final String functionKey,
 123                               final Source source,
 124                               final String mainClassName,
 125                               final Map<String, byte[]> classBytes,
 126                               final Map<Integer, FunctionInitializer> initializers,
 127                               final Object[] constants,
 128                               final int compilationId) {
 129         return store(functionKey, source, storedScriptFor(source, mainClassName, classBytes, initializers, constants, compilationId));
 130     }
 131 
 132     /**
 133      * Stores a compiled script.
 134      *
 135      * @param functionKey the function key
 136      * @param source the source
 137      * @param script The compiled script
 138      * @return The compiled script or {@code null} if not stored
 139      */
 140     public abstract StoredScript store(final String functionKey,
 141                                        final Source source,
 142                                        final StoredScript script);
 143 
 144     /**
 145      * Return a compiled script from the cache, or null if it isn't found.
 146      *
 147      * @param source      the source
 148      * @param functionKey the function key
 149      * @return the stored script or null
 150      */
 151     public abstract StoredScript load(final Source source, final String functionKey);
 152 
 153     /**
 154      * Returns a new StoredScript instance.
 155      *
 156      * @param mainClassName the main class name
 157      * @param classBytes a map of class bytes
 158      * @param initializers function initializers
 159      * @param constants the constants array
 160      * @param compilationId the compilation id
 161      * @return The compiled script
 162      */
 163     public StoredScript storedScriptFor(final Source source, final String mainClassName,
 164                                         final Map<String, byte[]> classBytes,
 165                                         final Map<Integer, FunctionInitializer> initializers,
 166                                         final Object[] constants, final int compilationId) {
 167         for (final Object constant : constants) {
 168             // Make sure all constant data is serializable
 169             if (!(constant instanceof Serializable)) {
 170                 getLogger().warning("cannot store ", source, " non serializable constant ", constant);
 171                 return null;
 172             }
 173         }
 174         return new StoredScript(compilationId, mainClassName, classBytes, initializers, constants);


 175     }
 176 
 177     /**
 178      * Generate a string representing the function with {@code functionId} and {@code paramTypes}.
 179      * @param functionId function id
 180      * @param paramTypes parameter types
 181      * @return a string representing the function
 182      */
 183     public static String getCacheKey(final int functionId, final Type[] paramTypes) {
 184         final StringBuilder b = new StringBuilder().append(functionId);
 185         if(paramTypes != null && paramTypes.length > 0) {
 186             b.append('-');
 187             for(final Type t: paramTypes) {
 188                 b.append(Type.getShortSignatureDescriptor(t));
 189             }
 190         }
 191         return b.toString();
 192     }
 193 
 194     /**
 195      * A store using a file system directory.
 196      */
 197     public static class DirectoryCodeStore extends CodeStore {
 198 
 199         // Default minimum size for storing a compiled script class
 200         private final static int DEFAULT_MIN_SIZE = 1000;
 201 
 202         private final File dir;
 203         private final boolean readOnly;
 204         private final int minSize;
 205 
 206         /**
 207          * Constructor
 208          *
 209          * @throws IOException
 210          */
 211         public DirectoryCodeStore() throws IOException {
 212             this(Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"), false, DEFAULT_MIN_SIZE);
 213         }
 214 
 215         /**
 216          * Constructor
 217          *
 218          * @param path    directory to store code in
 219          * @param minSize minimum file size for caching scripts
 220          * @throws IOException
 221          */
 222         public DirectoryCodeStore(final String path, final boolean readOnly, final int minSize) throws IOException {
 223             this.dir = checkDirectory(path, readOnly);
 224             this.readOnly = readOnly;
 225             this.minSize = minSize;
 226         }
 227 
 228         private static File checkDirectory(final String path, final boolean readOnly) throws IOException {
 229             try {
 230                 return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() {
 231                     @Override
 232                     public File run() throws IOException {
 233                         final File dir = new File(path).getAbsoluteFile();
 234                         if (readOnly) {
 235                             if (!dir.exists() || !dir.isDirectory()) {
 236                                 throw new IOException("Not a directory: " + dir.getPath());
 237                             } else if (!dir.canRead()) {
 238                                 throw new IOException("Directory not readable: " + dir.getPath());
 239                             }
 240                         } else if (!dir.exists() && !dir.mkdirs()) {
 241                             throw new IOException("Could not create directory: " + dir.getPath());
 242                         } else if (!dir.isDirectory()) {
 243                             throw new IOException("Not a directory: " + dir.getPath());
 244                         } else if (!dir.canRead() || !dir.canWrite()) {
 245                             throw new IOException("Directory not readable or writable: " + dir.getPath());
 246                         }
 247                         return dir;
 248                     }
 249                 });
 250             } catch (final PrivilegedActionException e) {
 251                 throw (IOException) e.getException();
 252             }
 253         }
 254 
 255         @Override
 256         public StoredScript load(final Source source, final String functionKey) {
 257             if (source.getLength() < minSize) {
 258                 return null;
 259             }
 260 
 261             final File file = getCacheFile(source, functionKey);
 262 
 263             try {
 264                 return AccessController.doPrivileged(new PrivilegedExceptionAction<StoredScript>() {
 265                     @Override
 266                     public StoredScript run() throws IOException, ClassNotFoundException {
 267                         if (!file.exists()) {
 268                             return null;
 269                         }
 270                         try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) {
 271                             final StoredScript storedScript = (StoredScript) in.readObject();
 272                             getLogger().info("loaded ", source, "-", functionKey);
 273                             return storedScript;
 274                         }
 275                     }
 276                 });
 277             } catch (final PrivilegedActionException e) {
 278                 getLogger().warning("failed to load ", source, "-", functionKey, ": ", e.getException());
 279                 return null;
 280             }
 281         }
 282 
 283         @Override
 284         public StoredScript store(final String functionKey, final Source source, final StoredScript script) {
 285             if (readOnly || script == null || belowThreshold(source)) {
 286                 return null;
















 287             }
 288 
 289             final File file = getCacheFile(source, functionKey);

 290 
 291             try {
 292                 return AccessController.doPrivileged(new PrivilegedExceptionAction<StoredScript>() {
 293                     @Override
 294                     public StoredScript run() throws IOException {
 295                         try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
 296                             out.writeObject(script);
 297                         }
 298                         getLogger().info("stored ", source, "-", functionKey);
 299                         return script;
 300                     }
 301                 });
 302             } catch (final PrivilegedActionException e) {
 303                 getLogger().warning("failed to store ", script, "-", functionKey, ": ", e.getException());
 304                 return null;
 305             }
 306         }
 307 
 308 
 309         private File getCacheFile(final Source source, final String functionKey) {
 310             return new File(dir, source.getDigest() + '-' + functionKey);
 311         }
 312 
 313         private boolean belowThreshold(final Source source) {
 314             if (source.getLength() < minSize) {
 315                 getLogger().info("below size threshold ", source);
 316                 return true;
 317             }
 318             return false;
 319         }
 320     }
 321 }
 322