CodeStore.java revision 953:221a84ef44c0
1/*
2 * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
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
26package jdk.nashorn.internal.runtime;
27
28import java.io.BufferedInputStream;
29import java.io.BufferedOutputStream;
30import java.io.File;
31import java.io.FileInputStream;
32import java.io.FileOutputStream;
33import java.io.IOException;
34import java.io.ObjectInputStream;
35import java.io.ObjectOutputStream;
36import java.io.Serializable;
37import java.security.AccessController;
38import java.security.PrivilegedActionException;
39import java.security.PrivilegedExceptionAction;
40import java.util.Map;
41
42/**
43 * A code cache for persistent caching of compiled scripts.
44 */
45final class CodeStore {
46
47    private final File dir;
48    private final int minSize;
49
50    // Default minimum size for storing a compiled script class
51    private final static int DEFAULT_MIN_SIZE = 1000;
52
53    /**
54     * Constructor
55     * @param path directory to store code in
56     * @throws IOException
57     */
58    public CodeStore(final String path) throws IOException {
59        this(path, DEFAULT_MIN_SIZE);
60    }
61
62    /**
63     * Constructor
64     * @param path directory to store code in
65     * @param minSize minimum file size for caching scripts
66     * @throws IOException
67     */
68    public CodeStore(final String path, final int minSize) throws IOException {
69        this.dir = checkDirectory(path);
70        this.minSize = minSize;
71    }
72
73    private static File checkDirectory(final String path) throws IOException {
74        try {
75            return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() {
76                @Override
77                public File run() throws IOException {
78                    final File dir = new File(path).getAbsoluteFile();
79                    if (!dir.exists() && !dir.mkdirs()) {
80                        throw new IOException("Could not create directory: " + dir);
81                    } else if (!dir.isDirectory()) {
82                        throw new IOException("Not a directory: " + dir);
83                    } else if (!dir.canRead() || !dir.canWrite()) {
84                        throw new IOException("Directory not readable or writable: " + dir);
85                    }
86                    return dir;
87                }
88            });
89        } catch (final PrivilegedActionException e) {
90            throw (IOException) e.getException();
91        }
92    }
93
94    /**
95     * Return a compiled script from the cache, or null if it isn't found.
96     *
97     * @param source the source
98     * @return the compiled script or null
99     * @throws IOException
100     * @throws ClassNotFoundException
101     */
102    public CompiledScript getScript(final Source source) throws IOException, ClassNotFoundException {
103        if (source.getLength() < minSize) {
104            return null;
105        }
106
107        final File file = new File(dir, source.getDigest());
108
109        try {
110            return AccessController.doPrivileged(new PrivilegedExceptionAction<CompiledScript>() {
111                @Override
112                public CompiledScript run() throws IOException, ClassNotFoundException {
113                    if (!file.exists()) {
114                        return null;
115                    }
116                    try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) {
117                        final CompiledScript compiledScript = (CompiledScript) in.readObject();
118                        compiledScript.setSource(source);
119                        return compiledScript;
120                    }
121                }
122            });
123        } catch (final PrivilegedActionException e) {
124            final Exception ex = e.getException();
125            if (ex instanceof IOException) {
126                throw  (IOException) ex;
127            } else if (ex instanceof ClassNotFoundException) {
128                throw (ClassNotFoundException) ex;
129            }
130            throw (new RuntimeException(ex));
131        }
132    }
133
134    /**
135     * Store a compiled script in the cache.
136     *
137     * @param source the source
138     * @param mainClassName the main class name
139     * @param classBytes a map of class bytes
140     * @param constants the constants array
141     * @throws IOException
142     */
143    public void putScript(final Source source, final String mainClassName, final Map<String, byte[]> classBytes, final Object[] constants)
144            throws IOException {
145        if (source.getLength() < minSize) {
146            return;
147        }
148        for (final Object constant : constants) {
149            // Make sure all constant data is serializable
150            if (! (constant instanceof Serializable)) {
151                return;
152            }
153        }
154
155        final File file = new File(dir, source.getDigest());
156        final CompiledScript script = new CompiledScript(source, mainClassName, classBytes, constants);
157
158        try {
159            AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
160                @Override
161                public Void run() throws IOException {
162                    try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
163                        out.writeObject(script);
164                    }
165                    return null;
166                }
167            });
168        } catch (final PrivilegedActionException e) {
169             throw (IOException) e.getException();
170        }
171    }
172}
173
174