Skip to content

Instantly share code, notes, and snippets.

@vybs
Created January 17, 2012 02:15
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save vybs/1624130 to your computer and use it in GitHub Desktop.
Save vybs/1624130 to your computer and use it in GitHub Desktop.
Sample code to rendering dust template using Rhino
package com.linkedin.dust.renderer;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.Scriptable;
public class DustEngine
{
public static final String MODULE = DustEngine.class.getName();
private static Scriptable globalScope;
public DustEngine(InputStream dustStream)
{
try
{
Reader dustReader = new InputStreamReader(dustStream, "UTF-8");
Context dustEngineContext = Context.enter();
dustEngineContext.setOptimizationLevel(9);
try
{
globalScope = dustEngineContext.initStandardObjects();
dustEngineContext.evaluateReader(globalScope,
dustReader,
"dust-compile.js",
0,
null);
}
finally
{
Context.exit();
}
}
catch (IOException ex)
{
throw new RuntimeException(" ERROR : Unable to load dust engine resource: ", ex);
}
}
public static String compileTemplate(String name, String rawSource)
{
Context dustContext = Context.enter();
try
{
Scriptable compileScope = dustContext.newObject(globalScope);
compileScope.setParentScope(globalScope);
compileScope.put("rawSource", compileScope, rawSource);
compileScope.put("name", compileScope, name);
try
{
return (String) dustContext.evaluateString(compileScope,
"(dust.compile(rawSource, name))",
"JDustCompiler",
0,
null);
}
catch (JavaScriptException e)
{
// Fail hard on any compile time error for dust templates
throw new RuntimeException(e);
}
}
finally
{
Context.exit();
}
}
public static void loadTemplate(String name, String rawSource)
{
Context dustContext = Context.enter();
try
{
Scriptable compileScope = dustContext.newObject(globalScope);
compileScope.setParentScope(globalScope);
compileScope.put("rawSource", compileScope, rawSource);
compileScope.put("name", compileScope, name);
try
{
dustContext.evaluateString(compileScope,
"(dust.loadSource(dust.compile(rawSource, name)))",
"JDustCompiler",
0,
null);
}
catch (JavaScriptException e)
{
// Fail hard on any compile time error for dust templates
throw new RuntimeException(e);
}
}
finally
{
Context.exit();
}
}
public static void render(String name, String json, Writer writer)
{
Context dustContext = Context.enter();
Scriptable renderScope = dustContext.newObject(globalScope);
renderScope.setParentScope(globalScope);
String renderScript =
("{ dust.render( name, JSON.parse(json) , function( err, data) { if(err) { writer.write(err);} else { writer.write( data );} } ); }");
try
{
renderScope.put("writer", renderScope, writer);
renderScope.put("json", renderScope, json);
renderScope.put("name", renderScope, name);
dustContext.evaluateString(renderScope,
renderScript,
"JDustCompiler",
0,
null);
}
catch (JavaScriptException e)
{
// Fail hard on any render time error for dust templates
throw new RuntimeException(e);
}
finally
{
Context.exit();
}
}
}
@gmills82
Copy link

Does this actually work with Rhino? I'm getting org.mozilla.javascript.EcmaError: ReferenceError: "window" is not defined. (dust-compile.js#21) errors once the dust-full-0.3.0.min.js file is read into the context object.

@vybs
Copy link
Author

vybs commented Jul 11, 2012

you will need the version of dust in

http://linkedin.github.com/dustjs/

We made it Rhino compatible.

@gmills82
Copy link

I really appreciate the quick response. I have one more question though. I'm new to Java and I don't quite understand what type of writer I would use. I'm just trying to compile the template and return a string of html data. What is the writer used for?

@vybs
Copy link
Author

vybs commented Jul 13, 2012

Any form of print writer should be fine.

Since we have the tomcat stack, we directly use the http response writer.

Here is how the render method is invoked

public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception
{
response.setContentType(getContentType());
PrintWriter writer = response.getWriter();
// converts a map to json using jackson json library
DustEngine.render(_name, new ObjectMapper().writeValueAsString(model), writer);
}

@gmills82
Copy link

Thanks for the helpful code snippet! I got it working.

Some tips for other that may attempt this that are also Java noobs like myself:

  1. InputStream dustStream can be a URL InputStream which points to the Linkedin dust.js file
  2. Don't use CompileTemplate and LoadTemplate together. I used LoadTemplate and then RenderTemplate.
  3. String renderscript looks funky... don't touch it... its right!

@vybs
Copy link
Author

vybs commented Jul 14, 2012

ah, some docs in my snippet would have helped ...duh!

So compileTemplate, just compiles and returns the js, calling the render after this, will not work

loadTemplate, compiles and also registers into the dust cache, hence calling the renderTemplate works

A better renderScript!

      String renderScript =
            ("{dust.render(name," +
            "JSON.parse(json), " +
            "function(err,data) { " +
                "if(err) { " +
               "writer.write(err);" +
             "} " +
             "else { " +
                "writer.write( data );" +
              "}  " +
            "});}");

@noroutine
Copy link

Hello, crowd

i really fell in love with dust.js so much, that made a filter, that you can use for web apps. For those interested: http://dust4j.noroutine.me/

It allowed us to write our dust.js templates in JSP, which is much more convenient, compared to manual compilation.

@noroutine
Copy link

And here it can be seen in action for those interested: http://zion.noroutine.me:8080/tobacco-demo/demo

@alohaTu
Copy link

alohaTu commented Oct 24, 2013

I'm so new with Java, and do you mind to help me how to use this sample to render dust template on server?
I want to try this your sample with Play framework, and I've created the package and add Rhino jar files to project, what is next stepd i need to do??

@giridharkn
Copy link

thank you........... :) i succesfully executed this code ... Dust is awesome.... :)

@vybs
Copy link
Author

vybs commented Dec 8, 2013

@giridharkn glad!. Also checkout the nashorn. It is 10 times faster than rhino
https://blogs.oracle.com/nashorn/

@vybs
Copy link
Author

vybs commented Dec 8, 2013

@alohaTu are you still wondering how to? Here is dust plugin you can use with play

https://github.com/typesafehub/play-plugins/tree/master/dust

https://github.com/jmparsons/play-dustjs

@alohaTu
Copy link

alohaTu commented Dec 12, 2013

@vybs : oh, you've came back, long time no see your comments. Thank you very much for your suggestion, but your link , it use for Play 2.x.x and unfortunately my project runs with Play 1.2.5, anyway thank you very much, I will find other solution. :D

@giridharkn
Copy link

@vybs thank you ... sure i try...

@sanky13
Copy link

sanky13 commented Mar 14, 2014

Hi All,

Tried to use the specific code and it seems to be throwing me an error . Am trying to invoke a dust template js and render it as a HTML using Java.
org.mozilla.javascript.EcmaError: TypeError: Cannot find function compile in object [object Object].

code snippet which i am trying to use is

File scriptFile = new File("./dust.js");
DustEngine DSE = new DustEngine( new FileInputStream(scriptFile));
DustEngine.compileTemplate("intro", "Hello {name}!");

@sanky13
Copy link

sanky13 commented Mar 15, 2014

Was able to by pass the initial issue, the reason for the above error was that i had not loaded the dust.js into the context, once that is done i was able to compile the template.js.

Now i am able to render the template but with no output. could you please let me know by seeing the below code what i had done wrong.

String renderScript =
"{ dust.render( name, JSON.parse(json) , function( err, data) { if(err) { writer.write(err);} else { writer.write( data );} } ); }";
renderScope.put("name", renderScope, name);
renderScope.put("writer", renderScope, writer);
renderScope.put("json", renderScope, json);
output = (String) dustEngineContext.evaluateString(renderScope,
renderScript,
"JDustCompiler",
0,
null);
return output;

@pawarkiran
Copy link

I want to use consilidate.js for rendering dust partials to logically place templates in different folders. How do I do that? Here is information on consolidate module:
http://stackoverflow.com/questions/12489739/using-dustjs-linkedin-templates-in-node-js-and-express3-x

But I would like to implement same on server side using Rhino. How do I link consolidate.js with Rhino?

@davidzchen
Copy link

@vybs Is there a Gradle plugin for linkedin-dustjs available?

@davidzchen
Copy link

Looks like there isn't. In case anyone finds this useful, I went ahead and wrote one myself: https://github.com/davidzchen/gradle-dustjs-plugin

@RameshRM
Copy link

RameshRM commented Aug 6, 2014

@vybs, is dustjs templating w/V8 is supported in java ? I dont find any links around it

@alidhra
Copy link

alidhra commented Sep 18, 2014

@vybs - Is there a jsr-223 code (ScriptEngine, ScriptEngineFactory) for Dust JS ? or are there any plans for it ?
If not, can one create the same using the direction pointed out by your sample ?

Thanks

@wuservices
Copy link

@vybs - with this solution, there's one global dust object in globalScope shared by every thread that uses DustEngine.render(), right?

If I'm rendering a large number of 'unique' templates (let's say millions), would the dust cache would get too if I used unique names? What's the best way to handle this scenario?

For any 'unique' template, I was considering always using a reused name like "UNIQUE-" + Thread.currentThread().getId() and calling loadTemplate before rendering each time, but I wasn't sure if there was a better solution since the cache might get larger with lots of threads. This seems better than creating tons of global scopes and disposing of them each time though. I did try using the same name for all threads and did observe problems due to the shared global context between threads.

This is a special use case for personalized text vs rendering pages where templates would be reused and more finite. Thanks!

EDIT: Updated strategy for threading after finding that using the same name between threads was not safe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment