Last active
September 23, 2018 21:16
-
-
Save chefhoobajoob/ea20097cf441a233292e923400e9b76c to your computer and use it in GitHub Desktop.
Possible solution to protecting module global data in js verticles from MT access
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* global vertx */ | |
var logger = org.slf4j.LoggerFactory.getLogger('vertx.tests.AddingVerticle'); | |
module.exports = { | |
vertxStartAsync: function (startFuture) { | |
logger.info('verticle started'); | |
vertx.executeBlocking(function (blockingFuture) { | |
logger.debug('requiring dependency'); | |
var adder = require('./onePlusOne'); | |
logger.debug('creating consumer'); | |
var config = vertx.getOrCreateContext().config(); | |
vertx.eventBus().consumer(config.address, function(message) { | |
var onePlusOne = adder.add(); | |
message.reply({result: onePlusOne }) | |
}); | |
blockingFuture.complete(); | |
}, function(result, error) { | |
error ? startFuture.fail(error) : startFuture.complete(); | |
}) | |
}, | |
vertxStop: function () { | |
logger.debug('verticle stopped') | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2014 Red Hat, Inc. | |
* | |
* Red Hat licenses this file to you 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 io.vertx.lang.js; | |
import io.vertx.core.AbstractVerticle; | |
import io.vertx.core.Future; | |
import io.vertx.core.Verticle; | |
import io.vertx.core.Vertx; | |
import io.vertx.core.spi.VerticleFactory; | |
import jdk.nashorn.api.scripting.ScriptObjectMirror; | |
import javax.script.ScriptEngine; | |
import javax.script.ScriptEngineManager; | |
/** | |
* @author <a href="http://tfox.org">Tim Fox</a> | |
*/ | |
public class JSVerticleFactory implements VerticleFactory { | |
static { | |
ClasspathFileResolver.init(); | |
} | |
/** | |
* By default we will add an empty `process` global with an `env` property which contains the environment | |
* variables - this allows Vert.x to work well with libraries such as React which expect to run on Node.js | |
* and expect to have this global set, and which fail when it is not set. | |
* To disable this then provide a system property with this name and set to any value. | |
*/ | |
public static final String DISABLE_NODEJS_PROCESS_ENV_PROP_NAME = "vertx.disableNodeJSProcessENV"; | |
private static final boolean ADD_NODEJS_PROCESS_ENV = System.getProperty(DISABLE_NODEJS_PROCESS_ENV_PROP_NAME) == null; | |
private Vertx vertx; | |
private ScriptEngine engine; | |
@Override | |
public void init(Vertx vertx) { this.vertx = vertx; } | |
@Override | |
public String prefix() { | |
return "js"; | |
} | |
@Override | |
public boolean blockingCreate() { | |
return true; | |
} | |
@Override | |
public Verticle createVerticle(String verticleName, ClassLoader classLoader) throws Exception { | |
init(); | |
return new JSVerticle( VerticleFactory.removePrefix(verticleName ) ); | |
} | |
public class JSVerticle extends AbstractVerticle { | |
private static final String VERTX_START_FUNCTION = "vertxStart"; | |
private static final String VERTX_START_ASYNC_FUNCTION = "vertxStartAsync"; | |
private static final String VERTX_STOP_FUNCTION = "vertxStop"; | |
private static final String VERTX_STOP_ASYNC_FUNCTION = "vertxStopAsync"; | |
private final String verticleName; | |
private ScriptObjectMirror newScope; | |
private ScriptObjectMirror futureJSClass; | |
private ScriptObjectMirror exports; | |
private JSVerticle(String verticleName) { | |
this.verticleName = verticleName; | |
} | |
private boolean functionExists(String functionName) { | |
Object som = exports.getMember(functionName); | |
return som != null && !som.toString().equals("undefined"); | |
} | |
@Override | |
public void start(Future<Void> startFuture) throws Exception { | |
String loadJvmNpm = "load('classpath:vertx-js/util/jvm-npm.js'); this;"; | |
String isolate = "loadWithNewGlobal({ script: \"" + loadJvmNpm + "\", name: 'isolated-jvm-npm.js' });"; | |
newScope = (ScriptObjectMirror)engine.eval(isolate); | |
newScope.put("__jvertx", vertx); | |
futureJSClass = (ScriptObjectMirror)newScope.eval("require('vertx-js/future');"); | |
String loadVertxGlobs = "load('classpath:vertx-js/util/vertx-globals.js'); "; | |
if (ADD_NODEJS_PROCESS_ENV) { | |
loadVertxGlobs += "load('classpath:vertx-js/util/nodejs-process-env.js'); "; | |
} | |
/* | |
NOTE: | |
When we deploy a verticle we use require.noCache as each verticle instance must have the module evaluated. | |
Also we run verticles in JS strict mode (with "use strict") -this means they cannot declare globals | |
and other restrictions. We do this for isolation. | |
However when doing a normal 'require' from inside a verticle we do not use strict mode as many JavaScript | |
modules are written poorly and would fail to run otherwise. | |
*/ | |
String requireVerticle = "require.noCache('" + verticleName + "', null, true);"; | |
exports = (ScriptObjectMirror)newScope.eval(loadVertxGlobs + requireVerticle); | |
if (functionExists(VERTX_START_FUNCTION)) { | |
exports.callMember(VERTX_START_FUNCTION); | |
startFuture.complete(); | |
} else if (functionExists(VERTX_START_ASYNC_FUNCTION)) { | |
Object wrappedFuture = futureJSClass.newObject(startFuture); | |
exports.callMember(VERTX_START_ASYNC_FUNCTION, wrappedFuture); | |
} else { | |
startFuture.complete(); | |
} | |
} | |
@Override | |
public void stop(Future<Void> stopFuture) throws Exception { | |
if (functionExists(VERTX_STOP_FUNCTION)) { | |
exports.callMember(VERTX_STOP_FUNCTION); | |
stopFuture.complete(); | |
} else if (functionExists(VERTX_STOP_ASYNC_FUNCTION)) { | |
Object wrappedFuture = futureJSClass.newObject(stopFuture); | |
exports.callMember(VERTX_STOP_ASYNC_FUNCTION, wrappedFuture); | |
} else { | |
stopFuture.complete(); | |
} | |
} | |
} | |
private synchronized void init() { | |
if (engine == null) { | |
ScriptEngineManager mgr = new ScriptEngineManager(); | |
engine = mgr.getEngineByName("nashorn"); | |
if (engine == null) { | |
throw new IllegalStateException("Cannot find Nashorn JavaScript engine - maybe you are not running with Java 8 or later?"); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var process = {}; process.env=java.lang.System.getenv(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var i = 0; | |
module.exports = { | |
add: function() { | |
i = 0; | |
i += 1; | |
var shortly_later = new Date()/1000 + Math.random; | |
while( (new Date()/1000) < shortly_later) { Math.random() } //prevent optimizations | |
i += 1; | |
return i; | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@RunWith(VertxUnitRunner.class) | |
public class Tests { | |
@Test | |
public void threadSafeJsVerticleFactoryWorks( TestContext theContext ) throws Exception { | |
String name = getClass().getCanonicalName() + ".concurrency"; | |
Logger logger = LoggerFactory.getLogger( name ); | |
Vertx vertx = Vertx.vertx(); | |
JsonObject config = new JsonObject().put("address", VertxHost.vertxId() + ".add" ); | |
DeploymentOptions options = new DeploymentOptions().setInstances( 8 ).setConfig( config ); | |
logger.info("Launching AddingVerticle"); | |
Async done = theContext.async(); | |
JsonObject deployment = new JsonObject(); | |
vertx.deployVerticle( "js-mt:AddingVerticle.js", options, theContext.asyncAssertSuccess( deploymentId -> { | |
logger.info("Done launching AddingVerticle"); | |
deployment.put("deploymentId", deploymentId); | |
done.complete(); | |
} ) ); | |
done.awaitSuccess( 30*1000 ); | |
if ( deployment.getString( "deploymentId" ) == null ) { | |
theContext.fail( "Couldn't deploy the AddingVerticle" ); | |
return; | |
} | |
ExecutorService executor = Executors.newCachedThreadPool(); | |
ArrayList<Future<Integer>> results = new ArrayList<>(); | |
for(int i = 0; i < 50; i++) { | |
results.add(executor.submit(() -> { | |
Async added = theContext.async(); | |
JsonObject addition = new JsonObject(); | |
vertx.eventBus().<JsonObject>send(config.getString("address"), new JsonObject(), ar -> { | |
if ( ar.succeeded() ) { | |
addition.put("onePlusOne", ar.result().body().getInteger( "result" )); | |
} | |
added.complete(); | |
}); | |
added.awaitSuccess(); | |
return addition.getInteger( "onePlusOne" ); | |
})); | |
} | |
int miscalculations = 0; | |
int correct = 0; | |
for(Future<Integer> result : results) { | |
Integer jsResult = result.get(); | |
if(jsResult != 2) { | |
logger.error("Incorrect result from js, expected 1 + 1 = 2, but got: {}", jsResult); | |
miscalculations += 1; | |
} | |
else { correct++; } | |
} | |
executor.awaitTermination(1, TimeUnit.SECONDS); | |
executor.shutdownNow(); | |
logger.info("Overall: " + miscalculations + " wrong values for 1 + 1, " + correct + " right ones."); | |
assertThat(miscalculations).isEqualTo( 0 ); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Vertx = require('vertx-js/vertx'); var vertx = new Vertx(__jvertx); | |
var console = require('vertx-js/util/console'); | |
var setTimeout = function(callback,delay) { return vertx.setTimer(delay, callback).doubleValue(); }; | |
var clearTimeout = function(id) { vertx.cancelTimer(id); }; | |
var setInterval = function(callback, delay) { return vertx.setPeriodic(delay, callback).doubleValue(); }; | |
var clearInterval = clearTimeout; | |
var parent = this; | |
var global = this; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment