001package com.killcoding.jsplus;
002
003import javax.script.ScriptEngine;
004import javax.script.Invocable;
005import javax.script.Bindings;
006import javax.script.ScriptContext;
007import javax.script.SimpleScriptContext;
008import com.killcoding.tool.CommonTools;
009import java.nio.file.Files;
010import java.util.List;
011import java.nio.file.Paths;
012import com.killcoding.tool.FileTools;
013import java.io.File;
014import com.killcoding.file.DiskFile;
015import com.killcoding.tool.CipherTools;
016import com.killcoding.file.BaseFile;
017import com.killcoding.log.Logger;
018import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory;
019import org.openjdk.nashorn.api.scripting.ClassFilter;
020import com.killcoding.tool.ConfigProperties;
021
022public class NashornContext extends JsPlusContext {
023
024    private NashornScriptEngineFactory nashornFactory = null;
025    private Invocable invocable = null;
026    private ScriptEngine scriptEngine = null;
027    private ScriptContext context = null;
028    private Bindings bindings = null;
029    private ClassLoader customClassLoader = null;
030    
031    private class SecurityClassFilter implements ClassFilter {
032        @Override
033        public boolean exposeToScripts(String className) {
034            if (allowAllAccess) {
035                return true;
036            }
037            return false;
038        }
039    }
040    
041    public NashornContext(String engineName){
042        super(engineName);
043    }
044    
045    @Override
046    public Object getBindings(){
047        return bindings;
048    }    
049    
050    @Override
051    public Object getEngine(){
052        return nashornFactory;
053    }
054    
055    @Override
056    public ScriptContext getContext(){
057        return context;
058    }    
059    
060    @Override
061    public void remove(String name){
062        if (bindings != null) {
063            bindings.remove(name);
064        }
065    }        
066    
067    @Override
068    public void put(String name,Object value){
069        if (bindings != null) {
070            bindings.put(name,value);
071        }
072    }  
073    
074    @Override
075    public Object get(String name){
076        return bindings != null ? bindings.get(name) : null;
077    }    
078
079    @Override
080    public void initScriptEngineManager() throws Exception {
081        ConfigProperties appenvCp = ScriptConsole.getAppenv();
082        String[] options = new String[]{};
083        if(appenvCp != null){
084            String engineNameMapKey = String.format("%s.options",engineName);
085            List<String> optionsArray = appenvCp.getArray(engineNameMapKey,new String[]{});
086            options = optionsArray.toArray(new String[]{});
087            System.out.println("Options: " + optionsArray);
088        }
089        customClassLoader = ClassLoader.getSystemClassLoader();
090        
091        nashornFactory = new NashornScriptEngineFactory();
092        
093        if (allowAllAccess) {
094            if (customClassLoader != null && customClassLoader != ClassLoader.getSystemClassLoader()) {
095                scriptEngine = nashornFactory.getScriptEngine(options,customClassLoader);
096            } else {
097                scriptEngine = nashornFactory.getScriptEngine(options);
098            }
099        } else {
100            ClassFilter filter = new SecurityClassFilter();
101            scriptEngine = nashornFactory.getScriptEngine(options,customClassLoader,filter);
102
103        }
104        
105        if(scriptEngine == null){
106            throw new Exception(String.format("Unable to create engine '%s'.", engineName));
107        }
108        
109        System.out.println(String.format("Engine Name: %s, Version: %s", 
110            scriptEngine.getFactory().getEngineName(),
111            scriptEngine.getFactory().getEngineVersion()));
112        
113        context = new SimpleScriptContext();
114        context.setBindings(scriptEngine.createBindings(), ScriptContext.ENGINE_SCOPE);
115        bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
116        
117        if (customClassLoader != null) {
118            bindings.put("ScriptClassLoader", customClassLoader);
119        }
120
121        invocable = (Invocable) scriptEngine;
122    }
123    
124    @Override
125    public void setAllowAllAccess(boolean allowAllAccess){
126        boolean changed = this.allowAllAccess != allowAllAccess;
127        this.allowAllAccess = allowAllAccess;
128
129        if (changed && scriptEngine != null) {
130            try {
131                recreateEngine();
132            } catch (Exception e) {
133                log.error("Error resetting access permissions: {}",e.getMessage(),e);
134            }
135        }
136    }
137
138    @Override
139    public Object eval(String scriptPath) throws Exception {
140        return eval(new File(scriptPath));
141    }
142
143    @Override
144    public Object eval(File scriptPath) throws Exception {
145        String script = null;
146        if(CommonTools.isBlank(scriptCipherAesKey)){
147            script = FileTools.text(scriptPath);
148        }else{
149            DiskFile df = new DiskFile(scriptPath);
150            byte[] encryptBytes = df.readAllBytes();
151            byte[] decryptBytes = CipherTools.AESDecrypt(scriptCipherAesKey,encryptBytes);
152            script = new String(decryptBytes,BaseFile.CHARSET);
153        }
154        Object result = scriptEngine.eval(script, context);
155        scriptEngine.setContext(context);
156        invocable = (Invocable) scriptEngine;
157        return result;      
158    }
159    
160    @Override
161    public Object evalScript(String script) throws Exception {
162        Object result = scriptEngine.eval(script, context);
163        scriptEngine.setContext(context);
164        invocable = (Invocable) scriptEngine;
165        return result;
166    }   
167    
168    @Override
169    public void close(){
170        if (bindings != null) {
171            bindings.clear();
172            bindings = null;
173        }
174        context = null;
175        scriptEngine = null;
176        invocable = null;
177        nashornFactory = null;
178        customClassLoader = null;
179    }   
180    
181    @Override
182    public String getEngineName(){
183        return engineName;
184    } 
185    
186    public NashornScriptEngineFactory getNashornFactory() {
187        return nashornFactory;
188    }
189    
190    public ClassLoader getCustomClassLoader() {
191        return customClassLoader;
192    }
193    
194    private void recreateEngine() throws Exception {
195        if (nashornFactory != null) {
196            ConfigProperties appenvCp = ScriptConsole.getAppenv();
197            String engineNameMapKey = String.format("%s.options",engineName);
198            List<String> optionsArray = appenvCp.getArray(engineNameMapKey,new String[]{});
199            String[] options = optionsArray.toArray(new String[]{});
200            System.out.println("Options: " + optionsArray);
201        
202            ScriptEngine oldEngine = scriptEngine;
203            Bindings oldBindings = bindings;
204            ScriptContext oldContext = context;
205            
206            if (allowAllAccess) {
207                if (customClassLoader != null && customClassLoader != ClassLoader.getSystemClassLoader()) {
208                    scriptEngine = nashornFactory.getScriptEngine(options,customClassLoader);
209                } else {
210                    scriptEngine = nashornFactory.getScriptEngine(options);
211                }
212            } else {
213                ClassFilter filter = new SecurityClassFilter();
214                scriptEngine = nashornFactory.getScriptEngine(options, customClassLoader,filter);
215            }
216            
217            if (scriptEngine == null) {
218                throw new Exception("Unable to recreate engine.");
219            }
220            
221            if (oldContext != null) {
222                context = oldContext;
223                scriptEngine.setContext(context);
224            }
225            
226            if (oldBindings != null) {
227                bindings = oldBindings;
228                scriptEngine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
229            }
230            
231            invocable = (Invocable) scriptEngine;
232        }
233    }
234    
235}