Java tutorial
/* * Copyright (c) 2014. * * BaasBox - info@baasbox.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baasbox.service.scripting.js; import com.baasbox.service.scripting.ScriptingService; import com.baasbox.service.scripting.base.ScriptCall; import com.baasbox.service.scripting.base.ScriptEvalException; import com.baasbox.service.scripting.base.ScriptResult; import com.orientechnologies.orient.core.record.impl.ODocument; import jdk.nashorn.api.scripting.NashornException; import jdk.nashorn.api.scripting.ScriptObjectMirror; import org.apache.commons.lang.exception.ExceptionUtils; import com.baasbox.service.logging.BaasBoxLogger; import javax.script.ScriptEngine; import javax.script.ScriptException; import java.util.HashMap; import java.util.Map; /** * Nashorn engine implementation * * Created by Andrea Tortorella on 10/06/14. */ class Nashorn { private static final String MAKE_MODULE_FUNCTION = "makeModule"; private static final String COMPILE_FUNCTION = "compile"; private static final String EMIT_FUNCTION = "emit"; private Map<String, ScriptObjectMirror> cachedModules = new HashMap<String, ScriptObjectMirror>(); private ScriptEngine mEngine; private ScriptObjectMirror mRootAccess; private NashornMapper mMapper; private long mLocalVersionCount; Nashorn(ScriptEngine engine) { mEngine = engine; mMapper = new NashornMapper(); mLocalVersionCount = ScriptingService.getCacheVersion(); } /** * Initialization logic */ void init() { try { if (BaasBoxLogger.isDebugEnabled()) BaasBoxLogger.debug("Initializing prelude"); // get access to prelude and save mirror. ScriptObjectMirror mirror = (ScriptObjectMirror) mEngine.eval(ResLoader.jsPrelude()); mRootAccess = mirror; mMapper.setMirror(mRootAccess); } catch (ScriptException e) { //fixme this should never throw throw new RuntimeException(e); } } /** * Script call evaluation * @param call * @return * @throws ScriptEvalException */ ScriptResult eval(ScriptCall call) throws ScriptEvalException { try { ScriptObjectMirror moduleRef = getModule(call); if (call.event == null) { return null; } Object result = emitEvent(moduleRef, call.event, call.eventData); ScriptResult scriptResult = mMapper.convertResult(result); call.validate(scriptResult); if (BaasBoxLogger.isTraceEnabled()) BaasBoxLogger.trace("ScriptResult: %s", scriptResult.toString()); return scriptResult; } catch (Throwable err) { if (err instanceof NashornException) { if (BaasBoxLogger.isTraceEnabled()) BaasBoxLogger.trace("Error in script"); Throwable cause = err.getCause(); NashornException exc = ((NashornException) err); String scriptStack = NashornException.getScriptStackString(exc); scriptStack = ExceptionUtils.getFullStackTrace(exc); int columnNumber = exc.getColumnNumber(); int lineNumber = exc.getLineNumber(); String fileName = exc.getFileName(); String message = exc.getMessage(); String errorMessage = String.format("ScriptError: '%s' at: <%s>%d:%d\n%s", message, fileName, lineNumber, columnNumber, scriptStack); throw new ScriptEvalException(errorMessage, err); } throw new ScriptEvalException(ExceptionUtils.getFullStackTrace(err), err); } } public Object require(String name) { if (BaasBoxLogger.isTraceEnabled()) BaasBoxLogger.trace("Required: %s", name); syncCache(); ScriptObjectMirror cached = cachedModules.get(name); if (cached == null) { try { cached = loadModule(name); } catch (com.baasbox.dao.exception.ScriptException e) { throw new RuntimeException(e); } if (cached != null) { cachedModules.put(name, cached); } } return cached; } private ScriptObjectMirror loadModule(String name) throws com.baasbox.dao.exception.ScriptException { if (name.startsWith("baasbox")) { return loadIntrinsicModule(name); } else { return loadUserModule(name); } } private ScriptObjectMirror loadUserModule(String name) throws com.baasbox.dao.exception.ScriptException { ODocument doc = ScriptingService.get(name, true, true); if (doc == null) { return null; } ScriptCall req = ScriptCall.require(doc); ScriptObjectMirror mirror = makeModule(req.scriptName, req.source); mirror = compileModule(mirror); return mirror; } private ScriptObjectMirror loadIntrinsicModule(String name) { String source = ResLoader.loadJsScript(name); if (source == null) { BaasBoxLogger.warn("Module not found"); return null; } else { BaasBoxLogger.trace("Module loading"); ScriptObjectMirror mirror = makeModule(name, source); BaasBoxLogger.trace("ModuleReady"); mirror = compileModule(mirror); return mirror; } } private ScriptObjectMirror getModule(ScriptCall call) { syncCache(); ScriptObjectMirror cached = cachedModules.get(call.scriptName); if (cached == null) { if (BaasBoxLogger.isTraceEnabled()) BaasBoxLogger.trace("Loading module: %s", call.scriptName); cached = makeModule(call.scriptName, call.source); cached = compileModule(cached); if (BaasBoxLogger.isTraceEnabled()) BaasBoxLogger.trace("Module compiled: %s", call.scriptName); cachedModules.put(call.scriptName, cached); } return cached; } private void syncCache() { long current = ScriptingService.getCacheVersion(); if (mLocalVersionCount != current) { cachedModules.clear(); mLocalVersionCount = current; } } private Object emitEvent(ScriptObjectMirror mirror, String eventName, Object eventData) throws ScriptEvalException { Object event = mMapper.convertEvent(eventName, eventData); Object o = mirror.callMember(EMIT_FUNCTION, event); return o; } private ScriptObjectMirror compileModule(ScriptObjectMirror mirror) { return (ScriptObjectMirror) mirror.callMember(COMPILE_FUNCTION); } /** * Creates a module from id and source * @param id * @param sourceCode * @return */ private ScriptObjectMirror makeModule(String id, String sourceCode) { ScriptObjectMirror o = (ScriptObjectMirror) mRootAccess.callMember(MAKE_MODULE_FUNCTION, id, sourceCode); return o; } }