Exploring how to use idiomatic JavaScript in Java applications with GraalVM

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everyone now with the sound my name is alex life and you're watching the next episode of the growl vm streaming week today we have a very exciting stream with a single topic that is all about javascript and all about growl gs the groviums javascript engine and i have a guest which makes me very exciting uh i have a guest and our guest is daniela boneta i hope i didn't bastardize your surname hi you can see that in the in the bottom right corner uh daniel of course doesn't see himself because he only sees my screen right but it's about javascript it's about tell me first like a couple of words maybe about yourself like um how like what you're working on how what what team are you on in the grand vm team and and so on yeah yeah so hi everybody uh i'm danielle boneta so the surname was pronounced very correctly and i am a principal researcher in the uh javascript team actually so most of my work is on the javascript engine i am actually i joined the project in 2014 if i remember correctly and since then i've been mostly i think like i've been working on a bunch of things but mostly most of my contributions to the project i've been on the javascript engine which internally we call the culture yes but it's called the javascript engine of ground vm or gravity and javascript this refresh it's not it's no gravity on javascript we had some some questions from from uh from various people like so growl yes is good growl js is good javascript and groundhog is also good i think for the contents of this session we're gonna call it growl.js uh that is the most convenient uh or just javascript because it is just javascript as the language implementation and it just runs in in in gram vm right we don't specify that like in for other for other things that javascript in the chrome browser or or javascript in in in some somewhere else right just only when things go wrong you have to specify what engine you are using right so yeah that sounds good to me yes uh and it's very exciting because javascript is a very popular language right and it's it's everywhere it's in the browser it's on the backend it's in the database uh it's it's everywhere and it sort of opens very interesting capabilities to share the same code and enable the same code run in very different environments which is very cool and one of them is of course one of such environments where you can run javascript code is of course java applications right so and for that one specifically really good solution is grav vm right so we can run you can you can embed your javascript engine in your java application and you can run javascript code and this is something that we would try to do during this stream and and i know a little bit of uh how it works and i know a little bit like i've seen some examples and tried this and that but daniel here is an expert and uh he prepared like some some demo applications that we would try to go through and develop so i don't want to show the code just i would like to develop the code and go through the stages so and allow you daniella to kind of comment on the particular particular like api choices or trade-offs or technological choices uh what people need to know when they actually when they actually will start using javascript in uh in their java applications so without the without further ado let's let's start maybe with with the actual uh with the thing i have prepared the i have prepared a simple small application let me just move move things around here a little bit because i think this is now a very reasonable setup so i have here a small application and it is a hell in an application it runs on my cloud machine uh which is a linux machine and it's very very simple it's literally the helidon se sample application is just like we need we needed for this setup we needed a java application so we can start adding javascript to it and show various interop uh possibilities and various uh other gauchos and so on right so this runs on my cloud this is a hell of an application i can run it i can start it uh maybe can you before we start right before we get into the details of the code uh can you maybe elaborate or maybe like describe what what we're going to do like in general what uh what's on the agenda yeah yeah yeah so on the agenda essentially the idea is that javascript is a very popular language for server side and in general web applications right and as you said like it's one of the widely like most widely used languages in the world and the second language like the second or like a yet another very very popular language is java so the idea is um why don't don't we try to combine the strength and the weaknesses or let's say the strength actually only of those two languages and the idea is to try to do that by building a web application that's actually using a pretty modern and cool uh server-side framework for the jvm which is halidon here and combine it with some cool uh libraries that can be actually that are very popular in the javascript ecosystem and those are not let's say directly right away available uh in the java space so the idea is like to try to combine both of them and essentially to be the reactive web application that takes some data massages the data processes data somehow and then uses some of the javascript uh libraries that are very popular to bring back to java some results and then to show the result back in the browser extension so you can call it a polyglot web service if you want and i think that's that's a good name for it i i think it's a it's a good name it's uh it describes the nature right like it describes the most unique aspect of this is that you are mixing two languages in a in a way that both of them use the natural they're native parts of their ecosystems uh in a sort of idiomatic way right so right here for for example here let's look at the application that i have it's a simple hello application there is one main class that creates the routing and starts the application server right and the routing is very simple so you just the greed service that is uh bound to the greed slash what and takes the parameter takes some input from the user and currently returns just the greeting message which is probably hello world so i will run this application now right uh and since i'm using this helen application right i will do the helidon run no helidon dev i'm sorry of the heavier and def this thing will allow me to run this application and it will initialize that and also it will listen to the changes that we do to the actual files so when we when we do that we will be able should be able to edit the code and the changes will be picked up automatically if you're interested if you're interested if you want to follow this along you can go to the github.com livestreams this is the repository where the code that we are developing currently is being mirrored so you can see there are some auto commits happening literally minutes ago so when we do changes the code is mirrored there so if you want to follow that uh we can we can you you can just clone it and fiddle with it right we have questions we already have questions in the chat right here right which is which is kind of interesting we will try to take them as we go because i think this is the most interactive way uh uh so and the question is from dimitri uh dimitri is asking will js have ability to run io like do http calls or access file system uh yeah that is uh that is the recurring question so the ends the short answer is that in that the ground js javascript engine itself is just a standalone javascript computer component with something that can execute javascript is very actually very complete and can run all the all the latest javascript features but it's a self-contained environment you can think of it as your javascript engine which will run in a in a web worker environment for example so if you want to have io in the system then what you need to do is you need to populate some of these environments with all these capabilities one way of doing this is to run node.js which essentially takes the js engine and exposes all the node.js i o capabilities into the engine whereas if you want to do that in a job application then you need to rely on what java offers you but we are not considering at the moment to exposing and introducing our own io libraries to to to the job to the graduates javascript instead we want to we want users to be able to essentially use the libraries that the java ecosystem already provides and actually today we're going to show one example one of the examples here how you can do io using the helidon web server and the having client in javascript essentially right oh that's not exactly exactly right so i think that answers the question uh sufficiently uh if not we can we can get to that point when we actually do show io but here i just showed that i can run this application right and we can uh we can we can we can see the results right so uh this is my application just application yaml there is uh aloha will be our greeting right so we can reload you can see that halidon restarted everything and we have our application our playground our sandbox so to speak here ready and we can start doing adding javascript to that we're gonna we're gonna i will i will operate right in this method i think this is this is a sufficiently sufficiently complex playground and i know some things so we need to create the context context is the uh right context is the the main thing for uh context the polyglot context is yeah so it might be that you're not actually running uh you didn't set your your java on time to grab vm because normally a context should be available i guess out of the box and yes so a context is essentially your entry point to all the languages in the graph vm ecosystem including uh for today javascript and so essentially it's enough to and you can think of it again back to the to the to the question of uh io you can think of it as a self-contained javascript runtime we can which contains everything needed to execute javascript in the in the context of this in the context of this of this demo so right exactly and so you should do context.create you already get a context that is capable of running some javascript code right so id context getcr dot wait i was in the presentation mode surprisingly i thought the font was a little bit small right so we create the context and this context is a class in the orgrelvium polyglot package so it is on the class pass when you're using ground vm automatically if you are building libraries what you can do you can uh add the dependencies from maven central uh and get those uh that api there through that thing and then you can email you can email the sources and for example i can do what i can do expressions right yeah exactly i can do maybe a function uh add and we d a b right and then we do a plus b uh return return yeah uh a plus b so we can actually write some javascript here um and we wrap that in extra parenthesis because this needs to be an expression right and then yeah i just uh i just i just create a value for that right and it can be a variable because we are on java 11. uh what doesn't it like well i think you have to provide this the language you want to because javascript because uh vm is polyglot so you want to exactly you want to say this is javascript right otherwise you could provide a source which is like a larger object that can contain all your sources from a file or from a literal or anything right uh exactly so so this is how i evaluate a snippet of javascript code or it could be different install component different code like python or r or ruby uh within the context that i just created and then i can use this returning value as as as a normal java object so it has a bunch of api so i can have for example i can convert that to certain classes and there are uh some built-in primitives that are supported like for example numbers and strings and uh functions and uh some other other like throw their date and duration and floats an instant and so on uh but since we're returning a function i can also execute that right i can execute and pass the arguments there so what i'm gonna do i'm gonna do and i know that what is not null what is never know right so i'm gonna do evil uh execute yeah and i'm gonna do what what and i think this is a spectacular demonstration of uh what neuralgias can do right so we take this function and i expect that we will have two strings concatenated together with with each other uh just right there so if i run this application again we will have aloha daniela daniela he'll fail it oh it cannot find the context which java java minus version so i'm running on ground vm so halidon in it oh no hello hell helidone death right right and we go to the ide like as simple as that uh this is in the main class in the id we can run it just normally here by running main uh i don't know what happened there with the helen thing and but we the server is up it is as fast as normally and when we do hello daniela we have aloha danielle right so this string is a result of javascript manipulation uh which is which is kind of very cool right and there are a number of things that you can do already with this so i think this allows you to be powerful enough right sort of this is enough to be dangerous right you can hold into javascript and you can also you can see that here we provide java objects as arguments to the javascript function so this is the basic of the interrupt of the interrupt kind of sort of api right um uh what are the are there other ways to kind of make java objects available to the javascript context context or maybe get something from javascript yeah so there are actually many many ways to do that uh for example another mechanism that you can use here in this example is uh so you can for example rather than than defining your function here as an expression you could just declare the function in the scope which means essentially you remove the parenthesis there and then we provide a mechanism to essentially read objects from the global scope of the of the digital machine of the of the javascript engine uh which essentially is called the bindings which is something that the context provides already so you should be able to do context.bindings i think yeah then provide the language id and then we should be able to just get uh one of the members of this of the global object which is called add and i'm and i believe that this will already give us an accurate equivalent functionality but at this time what we're doing is actually we are reading from the global context a function named add and then we are calling it functions so essentially same functionality but we are accessing the function in a different way so we are not we're no longer relying on javascript questions we're just getting the function as a as a normal executable guy and we're just calling it out right so it just sits there in the top in the in the top uh name space right right in the um is also also available like i can put things in the bindings right so if here i do put something i can put a member and sort of expose the java uh a java object to the javascript functionality so like i could put a logger into that and then have a logger used in this add function right yes exactly oh so that is neat that is neat yeah and by default we provide like automatic conversion as you actually said already we provide automatic mechanisms to to convert some of the common types or some of like the java types back and forth to javascript so in this example you don't really have to you know take care of converting this or that argument down to the javascript type or convert the return value back to java this is i think this is like good enough for a lot of applications already but of course we provide like a lot of mechanisms that can be used to customize how java objects can or cannot be exposed back to the to the user or the javascript code for example sorry uh one of these we have many many mechanisms one of these mechanisms is called boxing and we have a package in the ground sdk that allows you to implement a number of interfaces called proxy interfaces uh that allow any java application to mimic let's say objects of the of the dynamic language in this case of javascript that we're running right so for example you can look at like proxy object or proxy array and that's proxy object right right and so any object yeah any implementation of this of this interface on the javascript site will be seen as a javascript object right so i could i could uh for example access its uh if on the javascript side i do like dot member it will go into the get member method by the key that is the exact right so this this is mimicking this is mimicking uh me making javascript objects that is very good can i can i also mimic java objects on the javascript site that makes no sense right well i mean in principle you can take a java object from the javascript side and just call it its members for example let's say you have a function your javascript class that's exposing a method you can definitely call this java method from the javascript side and from the javascript point of view that will look like a javascript object that you are then calling calling out right uh so absolutely so that that is like so as i said we provide many different mechanisms to customize how our objects and classes are exposed to javascript applications um by default uh all the public uh methods of a public class are exposed out of the box to them to the javascript um engine as soon as you expose you you share them with the engine so for example you pass them as an argument or you define them in a global scope but there's there is one thing called access that you can use to actually customize how this behavior work and we provide also annotations that can be used to selectively annotate certain fields of a class that you might or might not want to to expose to the to the javascript uh application right so if i have a sort if i have a class like okay this is my main class if i want to make it exposed i can do host access not sure i can do host access i had the host access just here right yeah uh that is interesting host access export right maybe you have to yeah yeah yeah yeah for class impro class right and then it takes something right i think that by default you can just annotate it but yeah what you can do is for example you can uh annotate this this uh private final fade greetings with export as well and the field should be accessible to javascript okay so this is the the sort of like fine-grained access uh access uh thing but i know like for the demo purposes and for for the thing what we can do we can do uh we can create a new builder right and we're gonna have the context and we can provide the options so what you can do what i will do here i will just say all access that i want on uh that i can have i want to have on my in my javascript context right so we create a builder this is the fluent api it allows you to it allows you to specify all kinds of options one of them is the access so for example you can allow you can see that there are like fine grained permissions you can have host class lookups and io and creating threads and processes uh other polyglot and native access but we will just go with the all access which is not the most secure option but uh in in this context this is the easiest to run right so now our javascript can have any all has full access to our host system so in this function i can for example get access to create new java threads or do some io and so forth uh but i also can do uh what what could be the next step here uh well the next step could be to uh actually start using javascript for building uh a web service maybe right so right through audio you want to go a little bit a little bit like deeper in the options that can be passed to the context no i think this is fine there are a bunch of options right and there are some some javascript specific options uh and there are some like execution specific options uh but mostly whatever yeah let's start building things i i i feel that i know now enough and i can start actually producing some producing some uh some code right uh what would that be oh we have a question from optimal monkey a question from optimal monkey will the proxy convert async js method to javasync methods and when creating js object will use the callbacks or futures uh so if if i get the question correctly the question is about how do we go from the java i think uh or promises to to javascript equivalent and essentially the answer is yes there is there are mechanisms to do that it's not through the proxy api at the moment currently we support this in a javascript way and we will actually give an example here in the in the rest of the in the rest of the of the of the day i think but in general yes so you are allowed to take any java um let's say object and to expose that as a promise by implementing a certain a certain interface that will be shown you can expose that java object as a promise to javascript code and there the javascript application will be allowed to for example await on something that's actually defined in the java world and the other way around as well so you're allowed to have a javascript async functions call them to register available uh reaction to the to the method so for example do this then that then that asynchronously and those reactions they themselves can for example perform something asynchronously and uh then when done continue the computation in uh back in into javascript and resume the javascript execution so interoperability between the two the two asynchronous words is is absolutely allowed not through the proxy api that is that is important right uh that was a very good question and i hope we're gonna touch on how to create uh promises and how the async interrupt works but for now let's do let's do a simple uh a simple do i have a client i don't have a client um maybe it's called cl like maybe a web client yeah it's supposed to be web client i i did that just before we applied yes so we're going to create a web client right and i would like to write i would like to query some api uh and then we're gonna try to process that data using javascript right and this is interesting because because apis usually return uh a lot of apis return json which is very natural to process in javascript so i know that you showed me this website earlier uh and this is the api that returns some uh information about beverages about beverages so we're gonna build this with come back we're gonna build this uh web client uh which is which will be our client right and we're gonna we can call it right we can do client get i think yeah what's it get yes get is to specify that you want to do an http get request that got me a little bit confused because it sends something that is like i was getting some you know future or something but it's actually the get method right and we want to get for a string uh and then we we d to to compatible feature yeah and there you go but right but this is a blocking get for yeah this is our result we're gonna query the beers from the punk api.com version two and we're gonna have that and for now we we can just let's put the result there uh and we're gonna do identity here and we're gonna have just the one return the element itself right yes just for the simplicity of the things and we're gonna do id right let's restart the application and see whether that works or not i don't think it works because it needs the exceptions on the signature but if we forget that for a second we can restart the application and then it will run will it run it doesn't run let's run it again oh we need maybe to uh wrap all this thing in the try catch right right that's yes probably the case uh catch exception we're going to ignore it right um ignore me right so we have it here uh now it should run now it should run and build class interesting expected right now it should build and run very good right so we're gonna rerun the main it works so now we're gonna uh try this and now it needs to load so it goes and it gives us the data right from some third-party web service uh how do i how do i now how do i process that with code i need to write some javascript and of course i don't want to write it here inside this string i want to refactor this right i want to extract uh like a constant i guess yeah that is a starting point for for very small javascript that's that's a good idea uh yeah right so we extracted the the javascript code it got all the way up here which doesn't bother me and then i'll just put it i'll just put the function there so this is the snippet that we prepared right yeah this is what we do so we have a function called parse beers that takes data and name right and we post data into beers and then we accumulate right this is one of the simplest things you can do with with with actually inviting javascript just taking some random json coming from the web parsing it extracting some useful information and then returning back this information to javascript and sorry to java actually to the to the heridone application here and to the user e eventually so what we do here is as you said it's pretty straightforward we have this function we we know that the web service we just we just created sends us back uh a string so we're gonna parse the string get the data filter the data by by the name that we care about that in this case was this this uh argument required to the great rest api and that's it right and what we need to notice here that we are back to the syntax where we actually uh evaluate an expression right so this code will return us this function so when we want to use it we need to go back to the uh to the to the previous way of doing things right so we evaluate that and then this eval this is actually a function so this is parse beers uh and we can call it this way because that is much more convenient uh so and then we execute the porous beers and we pass the result of whatever we got there uh and then for some reason we greet them but we let's not read them let's just return this one string right uh so this is just the message right string message equals parse beers because uh this is the oh this is actually a value yeah so you can do for example dot to string if if we're returning you're going to talk to the string yes and you can string or s string you can do both in this case right because because it's a string yes it's it's uh it's it's returning as uh json.stringify so it creates a string and um and now if if i see your code correctly here you you're returning an object by uh the json object field there but actually we do that in javascript so because we we already create uh an object via by uh json.stringify so i think we can just respond that end the message and we should get back something that uh is a well formatted json object let's see right so this this should should be much simpler code here indeed because if we have javascript there already why do we need to why do we need to compress here so we have hello what right so there are no gears called yeah i think that the reason is there are no beers called what so let's try to see if there's a beer called ale like ale ale okay ale because right no we don't get a beers either so there might be so either the website is already blocking us or or we need uh we need to debug this and see what's what is actually not working here right oh let's debug that let's do it like that so obviously i can that actually what i wanted to show anyway uh right so i can i can debug the java code here right this is normal and this is expected because this is normal java application but it can also debug my javascript code so this one and in order to do that there is this particular trick which you never remember out of the box but i have it somewhere as a snippet uh so what we need to do we need to sort of we need to specify the options i think uh we need to specify a couple of options on the context uh and one of them is for example in i'm not sure if that's double dashes or uh i think you don't you don't have to pass in double dashes i think at this here you can just provide the name and if it's like a language uh option then you might want to prevent it with like gs. something um but in this case i think i think it's it's not maybe maybe it works uh already but i don't think it's right yeah it was inspect uh true right can you double check what i'm typing so and then like from the top of my head i think it's the inspect like the port and then the let me double check it so yes so you have to provide the port okay and then you'll have inspect path right right and here i do i needed to what was the situation i had the i had that snippet somewhere let me just find that um very very briefly right so i'll just do inspect path and then just run them in uid right something like this show we do the java and this needs to be string this needs to be string because we operate options are strings right so now we say that we want to start our contacts we want to start it with the debugger uh attached so we're gonna run this application and the server is up i think we need to touch this thing once so we touch the thing once right and now we need to now now we need to run the debugger right so we did the debug and we do edit configurations and uh this is my intellij idea right so it knows how to debug javascript uh wait let me exit the right so it knows how to debug javascript uh so i will denote nodejs right and we d no i need the remote node there's chrome remote if i remember correctly right so and then i do just my port 4242 uh and i think that's it so we're gonna hit the debug the debug thing opens here should open here and it should open also a file for us at least it did that before before we try that resource cannot connect why connect it connect debug connect again connecting to localhost 4242 yes so maybe because we're starting with a context that was already created upfront so maybe we can we can start in debugging mode directly so we can basically stop it and start it again right wait stop right oh yeah because we already touched it first right so we run the application we run the application it's there and then we attach the debugger yeah and now you you since we are we are creating the contacts from within the getter you should actually get something so like in the url i think inspect path string port inspect path can we connect a localhost well that's a shame uh it might also be if you have some some wrong configuration can you maybe try just with the java debug that is the first thing yes let's put the normal java breakpoint and just do the uh the normal java debug configuration run debug debug uh just debug main just open rerun um so we are there and uh yeah let's put the breakpoint here right and then we need to access this we access this and then we should see the things in our id right so we're sitting on this on the breakpoint and then what we step over we stop over the context right now it takes a while because we're building the context in debug mode and maybe and then we through like we jumped out of the thing i think we got uh do you understand what's happening so it seems to me that for some reason we are not even not even so maybe i would double check if the javascript code that we are executing is is actually well formatted because it didn't seem that well that looks fine i think right so the code looks fine it's a function we open the parentheses we do the things that's a shame because it's supposed to be so right now the so i think the problem with calling the function is that we're providing one argument rather than two uh the problem with the debugger i think it might be maybe an issue a configuration issue in your in your setup or something like that because the function here prosperous takes data and the name and in our java code we are actually providing in if i'm if i'm not wrong just the data or just the name uh when we run the javascript we execute yeah we just execute result which is a string right and we also want to pass in the name that we actually used so the request from the user that was the what parameter i think oh okay yeah that is that maybe is the problem right so let's stop the debug [Music] let's stop the run and rerun this uh the also we could actually lock the exceptions yes uh i think that is a very uh good practice see that was the ignorance of me trying to so the server is up we are running now it sits behind the debugger right yeah i don't know if you run it again or like in normal no no i'm i'm fairly sure that this was my attached note right apply unnamed 4242 debug right do we see do you see what's happening debugger we're connecting but are we are we maybe still waiting on the java debugger no no i think i run without the debug right the server is up right let's do the things let's remove the javascript debugger maybe there's a promise i promise it worked i promise i think i saw it there's there was a typo maybe in the in the configuration we provided just try to roll back wait rather than inspect there we go that is very true oh my god that would be the silliest thing ever uh right so what do we do what do i do now i run this so now i can do i can do the debug let me show you the debug i want to get the debug running debug i know you want we're connecting we're connecting to this we're going to debug this javascript even if we sit here all night no that's not true no we're going to leave it at some point of time a question a question from optimal monkey as well when quoting one language inside another can we avoid the risk of an injection attack like uh for example one way would be only to allow constant constant expressions to be passed uh this will also allow language compilers to be only invoked at compile time uh do you think well that that is a good question i'm not a security expert so i don't have like a strange as a strict answer here but in general what we allow the way we are we we build security around around the context is to uh it's like to give the context capabilities to allow or disallow things and so for example as we discussed before you can allow uh certain lookups into certain in certain java fields java objects and so on or for example we can prevent a context from using from doing io or from performing uh https and so on so forth so even in principle even if your code is not trusted if your context is configured in a way that you can prevent very bad things from happening like for example starting a process or performing uh bad requests that is actually the context is already let's say a good first line of defense oh we have this and the service is working now i see right just to add to that there are also a number of a number of options that you can specify on the truffle level right that enhance the security so you can sort of limit the resources that your embedded language has access to so you can for example specify how much ram it takes or how much how much how many statements can it execute or the timeouts uh stuff like that that is a feature of a graph of enterprise but it is possible to kind of restrict uh also what your what your embedded language is doing that way yeah absolutely uh but uh for constant expressions i think uh you you can also do if you really know what you want to do with with that you can create a small language based on truffle right then just allow like a number of expressions so you can create a very simple language that only allows constant expressions so for example one example for that is i think and this is i don't think many people know that but there is a regex implementation as a truffle language uh called t t regex right so this is the truffle language sort of the same way as we do javascript here that allows you to specify and it like compiles and runs regular expressions which is not exactly what you're asking optimal monkey but i think it's an interesting tidbit of information as well right so we figured out that the very basic code usage here is uh you can see the service actually runs uh so there is no ipa but ale is there right so we haven't one ale in the database there in that third-party service so we run that yeah right but now now one of the things that i would love to do with this right is i would like to make it more readable the code here in the string is not that is not the best practice right it cannot be the best practice so can we make it more yeah absolutely yes so the way the way you would do this is normally in javascript you will actually take this and refactor this code into into a module that you then can you know use and reuse from multiple sources it's a pretty straightforward um concept right and the way and the way we do this in in graduate yes is with so as i said before rjs fully supports many uh new accuracy features actually actually graduates is very very very very up to date and and top notch now let's say from the point of view of the language feature supported so we support all new features of the of the javascript language including ecmascript modules or esm for for sure so the way the way we support them we support them mathematically so there's nothing very special to do you just need to take this javascript code uh put that in a file uh by con by a convenience and we call the file should be called dot mjs so that the engine knows that the file is a module and can execute that as a javascript x module that is not strictly required because for example you can configure the sources of the of the module to be to have a specific name type but in this case we can just we can just create a file call it um yeah our spears that sounds good and then we can just plug plug the the function and put the function in from our javascript code exactly right so i will take all that and just put into the file uh and i i think i need to i can wait i need to on i need i need to un escape that from a string i'm in javascript yeah so here i added the name here as the construct uh and right so this is the same code right we parse the beers and uh have the name and then we do this i don't need this string actually here yeah right um so it's just one function and we export that from this module this single function so now we can go and simplify this code here and make it use that particular javascript ecmascript module how do we do that yeah so the way the same would there was a very same way we were executing code through a context in the previous case we can do that now but we need to tell actually where the sources are so we need to load the sources into the context and that can be done by an api called the source which actually we can use to be uh sources from file for example so in this case we want exactly very good um and so the way we want we're gonna do that is we're gonna essentially first of all and and the reason we want to do that is actually i should probably give a little bit of background of javascript background modules so whenever you want to load the module in in javascript if you want to load the module using the import statement which is one of the ways you can go the module you need to be able to load the module from another mode let's say simplify and so what we're going to do now is we need another module that just loads this the our own module and executes it and so one way of doing this is as you were doing uh by creating a source that actually contains some uh some javascript literal that loads the the actual module that we that we created so one way to do this is to create a new builder right and then you provide i would provide that we can provide the sources uh into this new builder so uh the language is just that that's correct right so here i just do a path to my module no there we want to put some javascript code that we can actually execute to import our our module just fine okay so i do module so we can actually do import and one way of doing this the function exported was called our spheres so we can just do this and correct so we can parse vs as something else like parts for example and then we can here right exactly full path uh let me just copy the full pass here uh copy path absolute path absolute pass is fine right so we're gonna import that from the absolute path and then because we evaluate now now we are doing it so now the source and this is actually this is i think is a fascinating so this source is a string source right because it would parse the string uh this is a string source uh so we will return the parse as the the last expression so we will have it on the other side when we evaluate this right and we also we need you said that we need to call it a module yes we need to call it exactly we need to give it an extension that's mjs for simplicity there as i said there are other ways of building modules but this is a pretty straightforward so let's call it that for the world yeah and then we'll build it right yeah so this is actually the source and now we need to eval this source yes right and even in the source we will get back a function now we get back this far spheres uh function uh parse the ears right so okay right so this is now what we do what we do and now pause beer says we don't need this parse beers right so but we still have parts viewers result what right right so this is now this is now what we do we created a single source is a single line of javascript that imports our modules from the local path in the file system and we when we evaluate that we're going to evaluate this input statement we're going to have this parse beers function as parse and then on this second line the second expression here we're going to just return that sort of so when we evaluate this code this value is actually this parse function so now i think this should work when we rerun our application it should do exactly the same thing but it should load the javascript code from the module file right yes and it works right right yeah we can even actually change the the sources in the javascript file to print i don't know hello from just to prepare the javascript to say this is a module plus json stringify i mean if you want to be 100 sure that this is coming from the source but otherwise mjs uh right so we are in the module and you can see that this is a much better way to write to write javascript right because now we have the syntax highlight we have the content assist we have all the things uh that are nice things that you can have and i assume the same way i can do like for example typescript and then just uh use the produce javascript files after the fact right so you can see that it's still yeah hello from mjs uh this is this is exciting so now this resembles an application that you can maintain much much better yeah uh which is kind of cool right uh can i do something with the past there yeah this is one thing that i didn't like very much yeah this is actually a very good question so of course of course like having all the times to force you to so in this way in this simple and simple uh line of javascript that we have here we are loading the file just providing a full path into the into the of the module into the engine this is of course not not the best right because you normally might want to load the file from somewhere else uh or you might actually have it uh i don't know from a jar file or you might even want to generate dynamically the sources sometimes that is actually possible so one of one of the mechanisms that's actually provided by by the gravity which is actually very powerful is the concept of a virtual so-called file system that is essentially uh [Music] let's say a component that the user can specify in and can actually tell a plug into the intel javascript context telling the javascript engine how a certain url or some url or certain path can be or should be resolved that can be useful for security for example if you want to prevent certain files from being accessible for example or in general they can be useful to customize to customize the way modules are loaded in this case so let's say for example you want to to to to have import parse beers from any arbitrary name like from full what you can do is actually you can create a beautiful system that just performs regular operations on regular files but that can intercept whenever the the engine the context is looking for food for something named full and then can hijack this this full um request for example to to another file system or as i said to our char resource or even generate the the the sources at runtime and return on a generated source uh at runtime again so and the way the way you provide this is pretty straightforward there's an option in the context creation so in context builder you can provide an option called dot file system right and then you need to implement an interface that that's part of our uh let's do let's do a class right uh i'll i'll create a new class i think this is uh the the absolute basic yes uh basic thing so we implement the file system and we need to implement them oops we need to implement the methods uh all of them i assume right right uh what's the best way to do that i mean you know it depends on what the user wants to do but for our goal here what we want to do is essentially we just for the for the sake of the demo let's say we want to delegate everything to the real system and so we provide and then as i said hijack only one specific keyword to load the file from from somewhere else also an actual consistent so for example what we can do is we can what we provide is essentially the default file system can be used here so we can create a field like private field uh like private file system uh default or delegate and there you can do file system dot um new default file system default function right and that will be right here yes and i can also i will also do the strings right so i will do private string this is string our uh let's call it specifier that's the the javascript name one of the javascript names or not just name module name okay module yeah module name right and this would be our let's say parspears right yes uh and we will do private string the path pass yes to that particular module and this would be our uh this would be our this full pass there right i can i think you can you can actually use a java path object because the file system will actually need a path object at some point so that can also be just java java right so my my my code will look porous beers here right so i will just call this module name right and then i will assume that in my file system i have the uh path right and we do path get just full path here right right and this is of course a simplistic example as sort of a simplistic example but we're gonna we're gonna hijack the module name resolution and when somebody imports parse beers we're gonna actually return that file so and this is a good example where you can use the same strategy for for example if you want if i want to serve my javascript from a jar file like as resources i would implement a file system that would uh take the module names and instead return say the bytes or the the uh channel to the actually module inside the jar file right right that's uh yes right so and i need to implement just a bunch of a bunch of methods uh right because i need first path i need to implement right so yes if that is actually the first function the first method that will be called by the engine as soon as as soon as we try to import from this parts beard uh keyword they will just look up here and we say okay give me a puff from from a uriel from a string actually right and then if not we just do the same the normal uh normal thing right so i just poured path this is again like wait yeah this is the same right it's the same just one string and the others with puff richard oh this is this path right right uh check access do we need check access i've probably no i think in book in no check access is we can just delegate to default but this is exactly for example one of the security features that i was talking about so here you can just say no i don't want to give you access to this so that's fine uh right so i just do actually i don't think that i have the uh a very good idea how to do this normally right i'm a copy of this from from the other snippet that you sent me earlier right so this is the very basic implementation of the file system right and you can see that it's pretty powerful right it handles the the accesses it handles directories it handles uh the directory streams it handles uh absolute pass and those all those things could be useful and could be called from uh within the context right so in the context there's could be some import to require that actually uh finishes in calling one of these things right yeah and it's actually important to mention here that the engine so growl just gives you absolute guarantee that everything will go through the file system so as soon as you create a context and you define your own file system you are guaranteed understand that all operations we go through through this through this uh file system right uh so and the by channel this is where we actually read the thing right so this is where we return actually exactly exactly this is where we return so for example we could on the fly create a in-memory file and return it if you want to generate sources on the fly or read from from a jar file as you said before so if we don't match we our our preferred module then we just return the thing and if we do match then we return uh what do we need to return uh so you could create just uh so given that we have the the hardcoded path we could just get right a file from the actual packet where we know the module leaves actually right so and we need we need to read it right yes what the what what doesn't it like do i need do you want the cast you need to return the the the the channel because the the interface acts with a bunch of sql bytes yes yes so okay so the channel so this is the very simple file system now right and we we created that and now we are using that from under the hood and our javascript code here is the very normal idiomatic javascript code so you can assume that this parse beers could be a normal module name that you otherwise would install from i don't know how you install how do you install ecmascript modules it's a very controversial questions for the javascript system so the the normal way is the most popular way is npm but there is a lot of debate in the in the npm in the node.js community whether uh you know modules should be asm or commonjs or amd so it's it's a complex ecosystem let's say like many things in javascript but yeah npm is of course the the to go place for modules what would you say would you say that the question of modules in javascript is it more or less controversial than the question of modules in java i think it's different because because javascript for a long long time didn't have modules and so people start i mean there are very similar analogies but at least there was a there was a kind of a standard that emerged in node.js which is the common js module and everybody was doing that in the in the server side and that actually influenced the also uh in a way also the design of of tecmo modules and so on so at least historically it was a little bit different but it is i would say pretty controversial as well um whereas in java there was not like a single you know i mean the web there are very popular module systems of course but there is not like one prevalent that to cover unions like common js and the controversy in javascript it is born from the fact that many people were actually using common js and so having them to switch to another module system yeah was a bit like uh but on the other end now now the standard liking java the standard has a module system so i it's pretty obvious that in the future having um which is asm which is what we're using here so it's pretty good that actually in the future having a module system in the standard that will will simplify things uh which is great i think for javascript right and so for the languages that depend on javascript like typestrike for example right right you we kind of forget that javascript is a actually a building stone in in very many ecosystems right and it's the like it's the only thing that runs in the browser right uh effectively uh yeah i mean there is also web assembly but uh i think it still needs to interoperate with javascript for certain tasks so javascript is the inevitable layer of uh of complexity that you have to work through right so our module works so our module works uh our file system there works and i think this is a very good example uh because it's it's a very simple interface right like we just implemented a couple of methods we can be more specific and just to return and read the path from inside the jar file which is i know is a sort of frequent question yeah a very frequent question uh what next can we can we do something else interesting with this yeah sure we can for example uh one thing that we can do is we can start um we can go back a little bit to the code and have a look at one quick uh quick place a one quick thing actually and and then we can start discussing a little bit how javascript and how we actually handle parallelism and concurrency in javascript and in the graduates engine in general so if you if you look what what this code is doing we are actually creating one context here the context is created the fair request right yes because this is forget magnetic and so essentially what's going to happen now is that for every difference that we get we're going to we will be creating one context we've initiated initializing events whereas in the end what we want to use is just a single function so one thing that we could do in principle is we could just move this context initialization somewhere else do that once and then just use the function multiple times that is something that would of course make sense from a resource management point of view right right and so okay right well let's do that yeah let's do that i know i know so we want to initialize that once right so the most the most reasonable way is to put them as the static things right static uh opposite source uh source right uh statics for source and then static uh value value right value is of course the yeah so now we have three static fields we will make them private static because we are very keen on encapsulating things uh but yeah but we can do that definitely we can do that now we will only have one version of context one version of the builder and one version of the uh parse beers function uh so now it should be should be faster right yeah right so let me just restart that and i will can i wait of course there's a catch of course right uh oh oh boy right uh i need the exception here okay okay i like right so we're gonna catch exception e and then we're gonna throw new runtime exception over e right and then in this block we're gonna do so now we'd actually we don't need these guys right we just only need value and we're going to do the context initialization here in the try catch block right i'm going to source insertion here and then we just do parse beers and we don't them here but we do here right porous beers equals context evil source right in the static initializer and this is the i know this is ugly right but uh this is the simplest thing yes uh and this is the simplest thing uh which compensates for the fact that we spent a little bit of time trying to debug the wrong number of parameters passed to the javascript function which actually was the problem because i took shortcuts in the previous catching exception block because i ignored that which sort of like i think this is maybe not ideal but like we're gonna we're gonna roll with it right so we're gonna run this application now i think it should work right we literally we did the same thing uh we do the same thing uh and our server is up and then we we return this thing and then it all works right so now that there's a catch now so this is actually the catch is that this is violating the model of of concurrency potentially violating the model of concurrent execution that we have in graduates because what could happen in principle is that as soon as you start sending a lot of requests to the hydrogen web service to our web service um especially if those requests are like a guy level of concurrency social multiple from multiple clients what could actually happen is that eleanor would would spawn a new thread editor internally around uh for example a thread pool and he could actually run multiple threads and so what will happen is that we will actually execute the same javascript function this very one function this part spears value uh concurrently from multiple threads uh this is not supported by by the graduating exam gym for good reasons and the reason the first reason actually i should say is that this is not uh allowed by the acting specification and one of the main goals of the javascript engine in principle is that we want to be 100 compatible with with the ecmaspec and so ekma specifies that uh concurrent access to javascript should not happen and indeed all the majors all the other major javascript engines like for example v8 don't allow you to do or spider monk you don't allow to do concurrent access to the javascript object so what we do in grav vm is actually we prevent this with with some runtime checks that actually make sure that your java code your multi-threaded java code cannot execute multiple the same javascript function concurrently because there could be so many other concurrency issues and databases and everything around concurrent concurrent programming in javascript because javascript for example does not have a java memory model it doesn't have a memory model for javascript objects so it's also unspecified what could happen when two javascript functions are called at the same time concurrently and so for example in this case if we start sending a lot of requests to the server graph vm will actually throw an exception and say no i don't allow you to do a concurrent access the same javascript object and the way this can actually be solved in the javascript system is very javascript uh javascripty i would say so we support multiple uh multiple contexts and multiple contacts can run the same uh in the same java application and on multiple threads of course this is an equivalent to let's say the web workers model in a way that also node.js has and all javascript developers are pretty familiar with and so what we will do what we will need to do here to fix this application is we will need to create or to use multiple contacts uh one per track for example but of course creating as we said creating context forever request is expensive so one pattern here uh is for example for example to create an object pool where you just take contacts from from java and you use them and as soon as you don't need them you you know it's an in subject you put them back to the pool uh alternatively for simple cases like this one uh another solution is for example to have a thread local and just create thread local contents strictly speaking this is not safe either because you potentially if you create a lot of threads and you never dispose your context you might end up with a memory but like for this case for example we can just take this this simple code and drop it into into the thread local uh factory and just get the thread local from from from our service and there we will be you know under concept guarantees that we are running the one contacts per thread and that is enough to fix our application right so okay i i hear you right so this is currently not not not safe because we can execute potentially can execute this javascript function from different uh java threads concurrently right which actually is it because the function has access to the globals and everything like nobody actually there is no specification what should happen right so to avoid that the uh the javascript implementation here will actually throw exceptions on the concurrent axis so what one thing i can do i can just the for example synchronize on the parse beers right here that is another option yes that is of course another option indeed so i can i can do i can do for example this and it's ugly but it will resolve the problem and now because parse beers is a single java object right it's a single value right now only one thread at the time can be inside this thing right uh executing code so now we resolve this problem so only one thread has access at the time uh which will not have the concurrent access but this is sort of ugly right so and the the proper solution would be to implement a thread pool not a threadball like a pool of objects just a pool of contacts i don't want that right but we are not gonna we're gonna we're not gonna execute the they create a pool that is actually bounded and then figure out how to multiplex from uh like a number of workers threats inhaled on because i i don't know how many threads helen will start uh so instead of that we will just do the thread local value right right yeah thread local value here uh and then uh i think it's like yeah wait i just maybe just threw it local not new just thread local right static function yeah there's a static method that you can use with like that local with uh with initial value within issue great right with initial and the supplier from nothing to what we want to put there and there is just this whole thing right yeah we can just return exactly yes yes you basically want to return this function a return for spears i think you can just return context eval and yeah okay um yes make sense make sense and then we don't need the static block so this ugliness becomes less ugly uh and then we don't even need to have this as static right so just because it's a thread local right so now what we need to have we just need we will at every access to the sparse bears we're gonna get either the previously initialized value from a thread or if it's a thread that didn't initialize it yet we're gonna initialize that with with the same snippet of code so now uh whenever we actually do this we need to do get and then that will allow us to resolve this concurrent access problems because now every thread gets one context for itself right which is i think a very elegant solution let's try this let's try this let's try this yeah it runs it runs it runs and we are okay right so we are there now so now we are using modules and we are also resolved we also resolve the concurrency issues so and yeah just to reiterate what uh daniella said right this is not the best because if your thread count grows then your context count growth which could be a memory leak and also you don't close the context right so context has a life cycle context has a life cycle so you can close the context and you should close the context when it's not needed anymore which means that this solution is a little bit too simplistic in terms of handling the context lifecycle so you should you should you should think about how to handle those and maybe create a little bit more sophisticated one and yes this is this is not that easy to create like generically on the say like for example truffle level uh but i think there is an issue somewhere on github so if you want to find that and thumbs up on the issue uh i think that might that might uh that might show that this issue is visible and people like it right so we resolved the concurrency on the java side what what about the concurrency on the javascript side right so that's actually a very good question so in javascript uh there is no there is no let's say there is no memory model which is not entirely correct because there is actually one memory model but that's not to access javascript objects is to access a specific class of objects called shared array buffers but i will not go into that so regarding concurrency actually there is a very very popular way of doing concurrency and specifically an unblocking concurrency in javascript which is javascript promises the reason is is pretty obvious javascript is used in so many places where actually you have a lot of concurrent uh concurrent interactions going on in the browser that is for example the user clicking in the server side that's pretty obviously uh for example http requests coming in uh and so javascript has a number of built-in mechanisms to deal with asynchronous interactions and to deal with multiple functions multiple let's say tasks and scheduling of those asynchronous tasks in a in a javascript context so what what we can do here is for example we can take take our module and maybe we can actually introduce an async function now uh what's your level of knowledge let's say of of promises and anything uh in general i've i've i've heard the things i've heard the words i've seen and handled promise error and a very good point i i sort of i think i sort of understand how it works right so it's just an object where to which you chain the execution but if you ask me to explain it to you like somebody who says they don't uh then i will not be so if you can like give me a quick recap uh that'd be that'd be great yeah so that is actually pretty simple so maybe one one way of doing this could be to just like uh type in some javascript or type in some java code actually in a like in another amplify just you know very very high level okay uh so it's just like new main class yeah right for example and there you can just create a new context with javascript context context ctx equals context [Music] create javascript right okay right and there we can just do context.eval of of some very very simple javascript code that i can actually well i can just just tell you to so just just say for example promise just type um new promise uh new promise okay right plus in a javascript anonymous function so function we have two arguments one is called resolve by convention and the other is called reject right and in the body of this uh no that's not actually so that that is correct for anonymous function so you you can remove the key the function keyboard and that would be it would be the same or you can open brackets directly there right so i did the arrow yes and then you open bracket and you put in the body of the function right and here what we can do for example is just we can just call uh resolve dot uh like sorry resolve with an argument as a function so we pass in 42 okay any value right any value and that's it and then uh later on you need to close the yes you need to put a bracket right and then here we can just uh for example say dot then another function that just prints so console.log that should be like that right i think that should be there should be it and then if we run this code uh we should see 42 uh right so we just we just run this main okay i hear you right so what we created created a new promise object that uh takes a like a function like a like a two to like a function out of two functions right resolving yes which is successfully finishing the promise and uh providing value instead of the promise object right so if someone takes like if someone chains then then and then we get that and i assume that uh for reject uh there is sort of a different api sort of like uh right yeah yeah catch catch and then uh and you clock right and then if you throw something from from or you call it reject sorry if you call reject then uh this this second console log will be will be called this is the api right and then the whole idea of the promise is that instead of actually doing the computation you immediately return an object right on the promise object yes and then anyone who wants to deal with the result of that computation they just change their logic using then or catch exactly right that is correct and that's that's essentially your your very nice introduction to promises essentially what you want to do is into this initial you want to call reject or resolve whenever you have a value and calling reject resolve doesn't have to be blocking uh like we are doing here in in a way so here we are just you know calling calling reject results in the body of the function but for example you could think of in in here you could register some asynchronous future tasks in java and then from java later on uh synchronizing of course on the on the proper in the proper way on the context you could call resolve and in this way you will call the reactions so called to the promise uh that was created here and so for example you could take this reject object and then pass it again like in in in whenever you you get some value for example in our in our web service call it and this is the way you would do the simplest way you could deal with the the promise api in in javascript and this is of course fully supported in english yes now there's another this is this is i think it's very powerful because allows you to really really deal with all sort of asynchronous scenarios and i think it's nice the way it specified in javascript because it's pretty straightforward in the sense that you have just done catch result and so on it's of course but it's still complicated to think about because you might end up in scenarios where you have a lot of inversion of control where you don't really know where to reject the word result so more recently uh the javascript specification introduced another concept which is called asynchronous functions which is essentially a mechanism to deal with the same scenario but in a more like uh imperative programming style sort of sort of way so so you essentially create an assisting function which could be considered a regular job to have a function that rather than a javascript function that rather than returning um a regular javascript value returns a promise that would then be resolved whenever the function is done doing this computation sounds sounds a bit like uh complicated but in the end it's pretty pretty useful and so for example here the equivalent of this code would just be an asynchronous function that if you want to resolve a value returns a value or if you actually want to reject the promise you can for example throw an exception uh but going back to our so this is like the the the promises like 101 now how do we interact how do we interact with these promised objects in in in javascript and gravian it's via intro so essentially what we can do here is we we can return the promise immediately and that will give us back a value and this value is a javascript promise and there we can call the then method on that value so if you do that so essentially you should just remove this dot then console log and dot catch console log and maybe we resolve the function rather than rejecting just because it doesn't make a difference but it's always good to resolve rather than reject then what this this will and and this will give us a value this x object and then we can do x uh invoke member right and here we do then so we kind of sort of manually uh manually invoke a thing and then the signature here would be we take the value and we do something so it should be uh a consumer right so it's a it's sort of a like what system yes we can just print out no no no not like that right uh system out print ln right and then i'll have the java and and then plus plus x right no it's can be x it's uh it's a value inside right inside right and then this is what's inside right in walk member is red why is it red i think you have to cast the lambda function or rename the reference why is that what can i even do what member i think invoke member was was correct yes so i think the problem might be that the java lambda might not be uh accepted so for example i will try to cast it to consumer object maybe right so it's a consumer of object yeah object wait uh yeah i cast casted cost and uh here i cast the whole the whole lambda right yeah oh because i think because the the types like the type inference maybe in first a different type there for my lambda maybe right so now this compiles right so if i run this then it should print java plus 42 and it doesn't right let's see why so that is unexpected thing do we need do we need to register then before the promise resolves uh no that should be that should be actually equivalent to the code that we've wrote earlier so that should actually essentially just uh just call resolve into this function do we need to invoke catch no catch is also is also not required here because we are just resolving a function so that should be fine [Music] let me we might have a typo or something yeah it could be uh do we need to do we need to extract this to something no that should also that should also work out of the box so maybe we can can you can we verify that actually the return value x using for example a debunker is uh is promised right yes but we can but actually let's let's do the the sync way because we we already mentioned that so let's actually change this from a new promise to an async function that is essentially the same thing like that yes and let's call it like a sync function full that just returns 42 right right and so what and what we have to do is actually yes so this this actually returns uh a function so we can do x dot execute okay that gives us rather than being a regular function that gives us a promise and on the promise we can then register then [Music] so this is the promise now and then on the promise we invoke then right i think this is decent right still doesn't print anything invoke member then this is then it should be then right yeah that is actually that is actually pretty easy do we need to enable some sort of uh options for input access yeah that is actually a good point right we are doing interrupts so we need to do you know we need to allow all yeah right that is a problem for some for some reason i was assuming that we're actually still using the the other context right there we go right so a success that is actually indeed a very good point so i uh it's always important here even if it doesn't entirely look like we are doing uh interrupt because javascript the engine is actually calling back java to resolve the promise so it's actually you know even if you don't see the call from javascript to the java lambda function so from this inside method java lambda that this consumer object we actually are because as soon as javascript promise is resolved that's what we this will call back john so we need to enable um uh do we need to tell the context look this is allowed so so we want to let you do that so yeah this is good actually the one last thing that i wanted to show you before that i think it's actually important to mention here before we go back to the service is that actually not only we can from java register uh an ip action to a javascript a synchronous interaction so in this case the asynchronous function creates let's say continues its execution in the java space but we can actually do the other way around which i think is also also very useful so we can from javascript wait or register let's say on on an asynchronous event to happen in the java space and which in javascript terms this is very natural so in the sync function um allows you i think functions allow you to await for javascript promises all for other javascript functions because they return promises so what you can do in this code here you can just say rather than return 42 you can have a weight um something and then return something all right of course you can adjust just return await something and this is something let's pass it in as an argument so let's say a way you write uh exactly do it do i need to like it's a function call right so i will pass a function called i think i think we can just pass in a regular regular yeah let's let's try them okay so the way we will pass the promise so here we need uh sort of a something that will be a promise right yes and the way the way we do we want to pass in something that's actually java so so the way this works that is actually able to resolve the function so the way this works in in in groujo yes is we need to define an interface for example so let's create an interface called uh called like promise continuation or dynabol or whatever and this interface needs to have [Music] needs to have essentially right exactly a method that takes two values takes on uh complete or sorry let's call it resolve and reject or unresolve on reject and these are actually the very same the very same methods resolve and reject that we are actually calling um back from from the previous javascript promise that was uh that we that we saw before and so essentially the idea here is that we can implement this interface with anything we want in java and then from java we can call result whenever we have uh whenever we want to do something and so for example here you can just pass in a function implementing this this like a java lambda that implements this this databall and then call unresolved dot 42 and we will get essentially the same the same code that we had above but we we are actually doing that from java so yeah you lost me i think he lost me so here i need to give pass uh an instance of this interface right so and that is a functional interest it's a lambda right which takes resolve reject right and then out of that it does uh just resolve dot so resolve is a value so resolve dot uh execute now i think you need to cast it to this then a ball class uh yes here like then able right yeah with yes right and then resolve is a value so you can execute with 42 42 let's say java 42. yeah and then right then we can take the for example we can take the bindings but well i mean we can pass it as an argument because we were doing that yes passing it to the execute right so and that becomes this pro uh for promise and then we await for that promise and then we resolve that promise and then we return that java42 so essentially essentially when we do that this this promise should be java 42. right so we just uh it's not even it's not a promise but we've returned the awaited value no we there's no performance is it a promise is this yes yes it is a promise so we need to consume the value from this consumer object the same way the same way and we will gonna do this consuming uh consuming the value right yes i'm a little bit suspicious if they yeah so we and that value is a lambda [Music] maybe we need to execute this like as a function let's try that yes this just does nothing right so essentially if you want so the thing is if you want to execute it as a function then it should not be casted as a as available it should be costed as a as a function like a java function in that takes um so let's let's try that we cast it to a java function that takes uh well and for example an object or whatever then returns a java performance so that returns actually our venable object yes not particularly following but java util function right and then we need to cast it to this right yes travel till function function and that is actually something that takes an argument so that can be for example maybe let me just send you the the thing once live right see this is like i yeah i think i understand the api here but i think i don't understand like like the knowledge about how to tackle this needs to be internalized uh fairly fairly fairly well right so somewhere in slack yes let me send it to you again i didn't buy okay oh boy right this is uh okay this is the other way around so let me just [Music] what is this uh so we have the functional interface here okay right uh java promise like does it need to be public uh that is probably that is probably the case yes again because of the context success that we used uh but then it needs to be in a separate file right but this is the same right public right move sendable to the animal java okay add uh and then okay x is unresolved it's like venable right right so this is our x and then we put that in the context and execute let me just copy this whole piece of snippet of text and then you you run run me through this right yes this is essentially doing that the other thing that we were that we were trying to do so let's first run it and see if it works okay oh yes there we go it prints so essentially yeah so essentially what what we are doing here and actually the same thing we were trying to do before but i think we were not able to do that because of the interface was not public so that was probably the problem but anyways what we do here is what i mentioned before so it's the other way around we are actually uh from javascript waiting for some asynchronous interactions that are actually happening in java to happen and so you can think of it as as if we were like stepping in and out from the javascript engine every time we have this await thing calling out to java into this java sync function that essentially is the the lambda x we we we define above this function actually is just resolving the promise with a value which is this v plus 42 and then finally once we we get this this free free results back into java uh sorry into javascript then we we just sum them up and we return back to java but given that we are calling in a synchronous function we are not returning just a value here i mean we're returning a holy group value which is not a javascript value but it's adjusted promise and so we have to register again into the continuation of the promise and so that's why we call invoke member then dot anything else and then eventually from java we are notified again back from the engine that the the synchronous computation in javascript happened now of course this is a little bit simplistic but the key the key message here is that this unresolved uh function uh in in our java lambda can actually be called from java so we can for example await in javascript for some very long running computation happening in another thread in java and then go back to javascript as soon as we are done and just keep doing our our working job uh that is absolutely absolutely possible and this is actually the way the way you would do asynchronous concurrency in in javascript right and optimal monkey asks is the javascript promise like is it returned to java as the java complete with the future or is it something no no we don't we don't have we don't provide a direct one-on-one mapping to compatible futures we map it to java value so sorry to the polygroup value plus there are multiple reasons one of the reason is that we want to let users be able to define and map the interface the promise javascript from this interface to the computable feature the way they prefer so for example you might want to do then you might want to register you know different you want to deal with different sort of asynchronous executions that you can have in java whereas in javascript and map that with javascript so for example you can have 10xing and so on and we don't want to provide a direct one-on-one we might actually in the future that that was discussed many times we might actually in the future provide some built-in mechanisms to provide uh a one-on-one conversion from a javascript promise to an arbitrary implementation but still leaving users giving users the possibility to specify the way they want to to have that and so for example in this case one would have to create a new compatible feature and then complete the future explicitly from this uh then method if you want to call that and then the competition would then again go back into javascript as soon as you register them so we actually have a lot of examples on how to combine the combine compatible features with promises in our in our documentation so i'm happy to post to post a couple of examples in the chat later but the key the key motivation is that and as i said that there has been there's been some discussion on providing automatic mechanisms so maybe in the future we might do that but it is already possible because we provide this this explicit way to customize it and and to for example use computable features but maybe sometimes you just might want to wait on you know something else uh but it is a good point that the computer features there is some overlap between the two the two apis right uh a question from anuk anu an anujik a question from a user in chat is this available on java 11 plus only or does it work on java 8 as well this is available on java 8 as well yes this is this is available in all the graph vm all the vm releases yes excellent i mean of course this is java 11 so we are just to clarify this is java 11 so we have part uh java pulse right but that is not because of of javascript so you could just use the value classes you know java eight and the thing will work out of the box of course right right i mean like if this would be java 16 just imagine then this could be a multi-line string right that would be that would be nice oh yes yes as soon as we have this this the support for that i'm trying it out but okay i think i understand sort of the promise like there are two main api things that we need to remember is they we declare an interface and then then and catch our three functions that we invoke and sort of async await is just kind of jumps in and out of the execution uh the syntactic sugar for like chaining the thens right yeah okay right so how how do we do that on in in our halidon thing well we can uh the simplest thing that i had in mind here that we could do is we could for example go back to our module and define an asynchronous function now and have i think i i sent you already like the snippet if you want but otherwise we can just build it here now at synchronous function it would be nice if this asynchronous function could wait on something so let's let's make this function wait on on on as we as we did right now let's make this function wait on something happening in java so we can write it i'll i'll paste the snippet uh and let's go through that because i think this would be a little bit easier uh so this is our porous beer function async right and this is what you what you thought about waiting so instead of passing the data we passed the the function the promise of the data uh we passed the promised data right very good yes exactly which will be resolved in java so the idea is we we i mean there is also a cache here which is always good practice to have in in a web service because otherwise we will be flooding the other the other external build service with with requests but besides the cache the key the key aspect here the key important thing an interesting thing here is that we are waiting on this fetchfield data which is essentially a java function that we do an http get for us and we give us back the data and then from there we can right so when we do the weight phage peer data uh it will wait for the function and then we'll get the data and that data will be an actual json that we're going to parse and then we're going to do something and then we say like oh esmodule we don't forget to set it into the cache uh and that's sort of it right uh this is very very simplification yeah i i like it i like it what do we need to do in on the java side to make it work we need we need the support for so we need that we need a then our then we'll interface but we already have it so that's that's done and then essentially what we need to do is we need to uh create this this um this function so if let's look at the interface of of the the javascript function we just defined it accepts two methods right right acceptable yeah we need to create this fetch beer data function essentially yes yes okay okay uh and since we are exporting the different name i took the freedom to rename that correctly here as well okay so here we need to before we pass the results so here with instead of the result we need to pass a function right that will yes a function that would actually perform the http yeah yes right so it's a function from uh just like we did right from object to thenable right no it uh can just be it can just be an implementation of this databall interface so it can just be a java java lambda that has two functions on resolve yeah and rejected of course of course of course so yeah yeah yeah how do i do like one thing one thing we do then double right then we'll [Music] fetch beer data right and then this is the function so on resolve on reject reject right so this is exactly what we what we want right uh and then we put it here right so we just need the implementation and that implementation uh on resolve on result yeah so we we're gonna we're gonna download the data so we want to perform this this this request then we want to resolve with with the value that we get that we got back from the from the website right so we send the date we get the data right and then we do on result yes uh execute execute execute void i think it can be also execute void because we don't care about the return value here we just want to and then in case there's an exception we can actually uh well we would be good practice to re to reject the promise here so we could do unreject dot execute void and pass in for example the exception or the exception message or yeah right right this is very clear i like this very much i like this more than rethrowing the exception so uh yeah okay okay so then you can now you think this would work well now no wait but it's still one one bit that is missing so we are actually in this response uh in this partspears.get execute we are actually calling it a same function so the result here will be a javascript promise will be asynchronous so we cannot just uh expect the the return value from this function to be a string and and blockingly send back the string back to the response method response.send method we actually have to do the same the same thing that you would do in javascript so we have to register uh then uh then object on the execution so after after calling execute we will get back a promise right okay right so this is not a string anymore right so we do execute uh invoke invoke member then and then we do this then we can pass in another consumer what did we do we did the consumer of object like this right uh so we pass a consumer and then we do uh response sent at value should be fine right so now we exactly now we get the sync the promise and then we're registered then whatever the promise will resolve then we throw it uh into the response i'm a little bit i'm a little bit a little bit uh not 100 satisfied that we are blockingly getting the complete future here but i will not ask because i don't feel myself uh ready to refactor this uh let's try to see let's let's see if this works right let's see if this works once uh we are running no we're running main two i want to run main one right so we are running this and then we are doing this okay we see from es module still the the the same information about the pale ale uh which is good good so now we are now we are actually doing like a very idiomatic javascript here in the in the parse beers right so there is a sync await function we export the same function we pass some java function that is uh asynchronous uh so we have mostly various synchronous here i would say yeah we can i mean we can still try to to a synchronize also the last bit as you said which is this this uh client uh that would actually require probably some some hard work in converting the javascript promise to a compatible feature but if you want we can give it a try or we can also just move on and anyway i think we will publish a blog post this is something important to mention we publish a blog post about this full async application at some point in the future and there the code will will be probably uh fully asynchronous all right uh right right right right uh there are a couple of um a couple of comments there on the chat which i've missed so let me just quickly go about that uh fabio says separate files better than multi-line strings which we totally agree but well absolutely has to be something new and exciting in java 16 right and that is multi-line strings just game there are plenty of goodness in java 16. uh right and then there was also a discussion and of pulling node.js libraries into java if needed uh followed by the discussion whether this is actually good or not uh uh but like i think there is a there is an example that you wanted to show uh kind of the final demo that we have is actually exactly sort of that uh and that goes yeah indeed that goes in the very same direction so even though it might be controversial as we said already in the beginning of our chat here it might actually make a lot of sense to sometimes import and use javascript functions it makes a lot of sense to use javascript functionalities from java whenever there is something that is you know very very very nice and well maintained and solid in the java ecosystem in the javascript ecosystem and you just want to call it from from java one example here that just comes to my mind because i'm also in general interested in you know data processing and data in general is the d3 library which is actually a very very very popular library among data scientists used by pretty much everybody to render data on the web in very nice nice way so so yeah so in in [Music] what we did here for this demo we put together a very simple uh javascript application that takes the d3 library and then just does what we we we were doing here with us queries an external web service takes some information from a public rest api via json parses the json date and then uses the d3 library to render this public information on actually an svg image and to show the svg image and return it back to them to the client um so the way the way this this works in practice in a javascript context so javascript context as we said before is not a node.js environment so it does not have access to all the uh node.js functionalities like for example required to load the npm packages but normally a package is more like a collection of different and different modules that can be common js modules esm modules and so on so the way normally javascript developers deal with these situations is they fund multiple npm packages into a single javascript file and then they execute this javascript file and this javascript file can be executed for example into a web browser so an environment where you do not have the node.js environment variables you don't have to require you don't have access to the node.js file system and so on so forth so as long as you take a library that is in principle um designed to work outside of node.js then you can bundle it plug it into our javascript particular context and then you can call it on there and use it from there and so what i did in this demo uh in this demo code that i mentioned before is essentially that i took a very very trivial but nice example of d3 server side rendering of an svg image from from the deeply demo gallery website actually and i just tucked that into into a single a single component and then i just called that component from from from a javascript context in in essentially the same application here and so the the halidon web service goes out fetches some json data and then we parse the data in javascript we render the data in a map and then we we just return the data back um actually the example should be like if you click on examples there should be actually the example i i think i used but it doesn't really matter see more examples right i think yeah servo gallery right so there should be one of the first examples this is like extremely nice and extremely popular library that really every data scientist is who wants to show some data on the web should consider using because it's extremely powerful and if you look exactly this is one of the examples and and if you look at the code here this is all pure javascript besides requiring uh the javascript uh the d3 library the javascript library that does this thing then from here uh essentially what this does it will just try to interact a little bit with the dom but not much and it will just produce this svg dot node here the last object so essentially um it is really straightforward to take uh some of the examples code here and then plug it in into into an npm package bundle and code the bundle from from the from the from our living web service right okay so there are steps and you sent me the steps before so before we like i will try to explain and elaborate on the steps because it requires a little bit of setup here so we have our uh module here that we are uh that we have some javascript in but this is the glue code so instead of porous beers async this would be our glue code here right and we need to actually i think you're looking at the wrong file uh no this is the wrong file yeah this is our default project right so i i need to i need to quickly navigate to uh to the d3 demo directory and i'm just doing that off screen okay uh uh right so let me just put it back here right uh so this is the project that you sent to me and there are two parts npm and maven right so right in npm uh we need to we want to use the dependencies and kind of sort of build the bundle with the browser if i right so i can do that um npm cd npm right um and you actually you've built the printmap module js yourself here but in a nutshell in a nutshell what it requires us to do is sort of run a bunch of commands like npm install d3 and pm install uh right json jsdom apn install text encoding installed browserify and then uh also installing the downloading the data for the uh counties which is the sort of a large uh large file here right so with yeah ls ll ls minus human here no wait what right exactly right so it's uh the data file is almost a gig or almost a megabyte and the print map module this is the javascript file uh which is the result of the browserifying uh of the thing is just sort of giant it's like two and a half megabytes no actually actually no this is the i mean the result by the result is indeed very big this print map module here is just my glue code that actually taking uh taking the letters and using them and this is very very good to see right this is your code using d3 right so this is the javascript code that uses d3 and we need to make it available somewhere in our project uh and it i think that what does it export it exports something right it should export some of course data supports a single yeah it explores this method called get map which essentially takes in a javascript array which we will fetch from from from a web service and returns back and it's an svg spg image and the last thing here is that if you look at this and you already saw that if you look at the size of this method this is like i don't know what is it 50 lines of javascript everything else the s3 library the js dom everything else is is already in a dependency that we just download and bundle together uh the bandwidth file that you were looking for should be in the maven folder already or you can actually maybe for the laptop yeah i will not build it myself uh so in the maven so if we do the tree here we can see that there will be bundle.js uh in the target classes uh and it's it's up there in the resources so what i will take i will take this beer bundle.js and uh wait the beer bundle yeah the parts beers is is from the previous demo code that i posted right so i need to take bundle and put it into my uh location so i will copy right so maven maven no source uh main resources beer bundle js right and i will copy that to the streaming setup streaming streaming setup live streams bear se uh source main resources right so we copied that file uh this is our browserified uh javascript of all the d3 and other dependencies that we needed right and then what i need to do i need to take this print map module js file right uh where was it uh in the npm right so and i need to copy this uh also to our things right now it's not needed because that that is not needed because that is already it's in the pro of the it's about it's the bundle model yes exactly right so that single javascript file is all we need to run around the demo right so here we just need to refresh reload from disk right so we have this part beers and no beer bundle.js right so beer bundle.js and now inside uh inside our porous beers do we need actually pressed beers we don't right we import d right we don't we can just use a liter right we can just use the literal in our code here because essentially what we want to do is we we have this bundle we want to load this bundle into the context and that can be done by uh for example just by creating a sort a new source and loading evaluating this source so we can do yeah right we do js and then just new files uh build right a new file and i just i full passed the bundle copy path copy absolute path oh i really hope this works this because this is such an interesting demo right so when we do the full pass right and then this is our source right so this is our source uh bundle source right and we need to evaluate that right uh so ctx ctx eval uh bundle source all right so now we have all the dependencies evaluated in our application and now we yes exactly so to use them so for example we can just uh remove this this import parse beer line that we have here right and we can replace it with essentially a little bit of magic but that is very simple so something that essentially imports requires the the resource that we bundle so i will just copy paste it into into slack so that you have it but it is as you would see it's really really simple oh you're already didn't okay yeah right because you already have resources of course yeah yeah right yeah so what you see here is we are essentially requiring something now it's important to say that require itself is not part of the javascript of the javascript context it's actually defined by the tool which is browser-refined in this case that we use to create the and browserify together with packaging everything gives us this required function which is capable of requiring only the resources that are needed for this specific uh module which includes by the way also the json object that that we saw earlier that is an object that includes and contains the the geographical topological data that we will use for for this example and then this this function is just exporting this this this simple piece of javascript code is just exporting a single function called this create map which essentially takes in some data and um parses the data to create an object and then just returns just calls in into library so the message here is that the user code that i've personally had to to write was this 50 lines of javascript blue code to get the d3 library plus these three lines of code here and then from java we can actually just call into this this source uh sorry according to the calling to this function and this will give us an svg image already and we can then render it into into html right uh so in in the last time we used the mjx mjs here because it was a module and we needed to uh import yes but here we don't do that so we can just say like oh it's a javascript thing uh right and we we evaluate the bundle we evaluate this source of our thing uh now what we need to do is there is one one last thing maybe so here this this function is so create beer map the javascript function that we have is just uh defined in the scope so eviling it will like like in in like earlier today will not give us the function because it's not an expression so what we can do is we can do context i'll get bindings and get the create beer map okay so we can first return it first right because you want to this weapon and then we can return this this uh get member uh this create beer map right and we do return that because this is the thread local thing right so we create the map let me just just in case just in case there are no typos right uh okay okay uh one thing that we need not to forget we need to add here a couple of options right we need to add the global property so we have the global uh that our dependencies inspect uh expect sorry yes yeah that is essentially to say that we define an object called global which very often which is a mapping of the global scope which very often node.js code expects so since we are running something bundled for node.js node.js packages might expect this global uh option to be to be there this global object will exist actually so that's why we have to provide it in but otherwise uh yeah the configuration should be the config is there now what do we need to do with the code uh so there's one one simple thing that needs to be done here we we're gonna query another web service because we need the geographical data about beers whereas this url we are creating just does give us names appears so essentially there was another there's another public website with with names exactly right and and and here we're actually querying this other web service with this what argument which is essentially the name of the thing we are looking for and then this with this we need to call into the into our our our module uh this is not a synchronous so our our module that we we could export on a scene function but we in the codable we were just exporting a normal function so you can just do functions.get to get the function and you just call it right away with the result coming from the exception so so here i have my parse beers get executed with the result yes right and this will give us this will give us the image if all those goes well and so then what we can do is we can essentially you know take this image and send it back to the client right so sort of in a very straightforward way we just say said they say they said the headers ate html set the status very simple html and here we will have the image which is an svg so it should it should render nicely uh and we do we need well i don't think we need to try catch right we don't even need right exactly and we don't need this so we just we just need the very basic stuff we create the client we query the third party api we've run it we could have added the cache here but we're gonna do this a few times the response i don't think they will see the traffic uh and yeah i think this this could be it right so now we're gonna do what we're gonna do we're gonna run this the moment of truth whether it will run or not it starts very fast and then on the first load actually it will need uh it will need to think about a little bit because when we well because there are actually yeah it's still loading because they're actually there we go because there are actually many many but then in the end the result is there and as you can see there are two two states in the u.s where a brewery is called the gram which is an interesting fact per se but besides what besides that what is actually going on here is that we are indeed queering an external web service that gives us all the names of brewery is called oh right oh that's close to close to home because i used to live in in massachusetts for for a few years and that's not far maybe maybe i forgot a brewery opened there but anyways so the interesting thing here is is indeed that we are actually generating the image on the fly with data coming from another web service and this is all done uh in in our engine using an external library uh so i i think this this shows pretty pretty you know pretty well the power of reusing existing libraries of course you could do this in java and there are plenty of libraries in java to do rendering of of all um all sort of images and by the way here we are just rendering an svg so a more efficient implementation for example you know take the svg create maybe a bitmap cache it just return the cash flow so this is not very efficient but you know it it shows that this is actually i think this is absolutely perfect right it what we did here like think for a moment here and uh if you're watching this like later think about what happened we took the d3 visualization library from the javascript ecosystem right it depends on some other libraries in the javascript ecosystem right uh there were there were a couple of dependencies there we fetched them with npm right we did npm install then we browserify them with with a simple glue code which is using those libraries from javascript right yeah and then we and by the way all of that yeah and the group code i just copy pasted it from the web i didn't i am not administ as free sorry a d3 library expert myself so i just went to the back to the public gallery of examples and i just took the code to generate this us mac because i would not be able to print a us mapping d3 myself and this is a group code exactly all the code that you see here i just copy-pasted it from the example we saw we saw earlier right so and this is something that you're like a javascript developer or someone who's working is working with visualization with d and then but what we did we did we created the infrastructure to run that from the microservice written the the written in halidon which our like java backend experts know and love uh i think this is this is a very very interesting possibility and as far as i know this is one of the closest things to actually reusing the like reusing the code in this polyglot like the value of polyglot one of the values of polyglot is oh we can reuse the code uh that we use for other in other ecosystems very efficiently and this is a a very neat example of doing that where there is just a little bit of glucose and there is the java ecosystem provides the libraries like for networking for concurrency for for handling the network requests http client and so on and javascript provides the the libraries for well what are excellent in javascript uh ecosystem for d3 is a is a is a very popular resource right so we have a question another question question in chat when we choose javascript engine uh uh is there a choice of running es6 or before uh i assume or maybe after as well uh right right that's a good question we we indeed have a lot of uh specific configuration options and one of these options allows you to specify the level of ecma specifications you want to have so essentially that the version the javascript version you want to run by default we run on the latest uh standard version so now it's ecma 2021 but it is an option to to actually run the engine for example in ecma5 compatibility mode uh that is that is possible yes uh there is a question from jill wooden how big is the beer bundle js um oh that's pretty huge it's i think it's in the order of 10 megabytes because if you if you're right if you're if you ls all the node modules that we're importing here might be maybe not not um not so there's a profile no no the file is the one we have in the resource in the resources folder in our menu right it's nine megabytes right which is i think this is this is nothing well it's it's uh it's i would say it's a considerable amount of javascript code because it's all javascript but it is it can be it can be more than that of course and again um it can be cached the source itself can be shared across and across context that's another thing that we that we mentioned earlier so you can create multiple contexts but you can share a single engine which is the underlying runtime so you don't if you create let's say 1000 contacts you don't have to create a thousand uh instances of this uh source file enough to have one source file share the contextual the engine so the underlying ground vmware on time and then everything else will be so also from an efficiency point of view nine megabytes of sources is probably something uh reasonable and by the way that so the the way this package was bundled was just by calling the hello world browser if i comment which does browser if i don't take this and create it create that using webpack or other bundling tools so one user has final grain controller what gets included or not just to say that i'm not 100 sure that we are including all the required packages for the demo into that into that bundle we are for sure including everything needed to run everything that d3 allows you to do entirely free library so maybe there are more efficient ways to do that yeah but at the same time this is on the back end right this is like you will probably run it in in in the micro service and like 10 megabytes here and there probably is not uh is not life changing for example i would i would assume that downloading 10 megabytes of javascript code to the browser could be could be a larger architectural problem that requires like browser caches and all sorts of other problems and solutions yeah that is one of the beauties of server side rendering right which is what we are doing here so absolutely uh another part of the question any idea of uh the js engine on which the js part runs on does it run on grounding directly a nuke that is the the only it runs on the ground vm directly so there is a truffle implementation of javascript uh which which runs on raw vm so it loads the sources it starts interpreting them and then it partially evaluates that specializes that and passes the graphs to the jet and then essentially runs uh on graph vm uh just like normally right thank you very much daniel uh we don't i don't see any more questions in the chat uh uh i don't see the questions in the chat but this is this is a spectacular example of a single application that that runs non-trivial examples of uh java libraries and javascript libraries in the same thing uh i think there is a lot of material here uh that's that's why i think i hope we're gonna we're gonna kind of sort of uh make an effort to re revisit those uh through articles and blogs uh and yeah i think that would be very very interesting uh from my side thank you very much for for this uh session i learned things i've tried things that they haven't tried before uh and i i enjoy doing this very much uh so thank you for hosting me actually and thanks in general for keeping this this stream very useful stream up and running yeah thank you very much if you have questions that and you're not watching this live then what you can do always you can find the the gradient community slack where uh quite a bit of uh quite a bit of uh of the team grunting team hangs out uh and where you can ask questions from from the team or from the like-minded individuals who are using uh various grapheme components in their real life or evaluation projects so that's where you would like to ask the questions and with that i would like to thank you once again daniel and i thank you everyone for watching uh i see you next
Info
Channel: Oleg Šelajev
Views: 596
Rating: undefined out of 5
Keywords:
Id: wQK-jn7L2lk
Channel Id: undefined
Length: 147min 57sec (8877 seconds)
Published: Thu Apr 15 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.