B4J Code Snippet [web]Velocity Template Language (VTL)

This example is based on https://velocity.apache.org/engine/2.3/developer-guide.html#how-velocity-works

Download additional libraries:
velocity-engine-core-2.3.jar
commons-lang3-3.12.0.jar
slf4j-api-2.0.7.jar
slf4j-simple-2.0.7.jar

B4X:
'Non-UI application (console / server application)
#Region Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
#End Region
#AdditionalJar: commons-lang3-3.12.0
'#AdditionalJar: commons-collections4-4.4
#AdditionalJar: velocity-engine-core-2.3
#AdditionalJar: slf4j-api-2.0.7
#AdditionalJar: slf4j-simple-2.0.7
Sub Process_Globals

End Sub

Sub AppStart (Args() As String)
    Dim Velocity As JavaObject
    Velocity.InitializeStatic("org.apache.velocity.app.Velocity")
    Velocity.RunMethod("init", Null)
 
    Dim Context As JavaObject
    Context.InitializeNewInstance("org.apache.velocity.VelocityContext", Null)
    Context.RunMethod("put", Array("name", "Velocity"))
 
    Dim Template As JavaObject
    Template = Template.InitializeNewInstance("org.apache.velocity.Template", Null)
    Template = Velocity.RunMethod("getTemplate", Array As String("test.vm"))
 
    Dim StringWriter As JavaObject
    StringWriter.InitializeNewInstance("java.io.StringWriter", Null)
    Template.RunMethodJO("merge", Array( Context, StringWriter ) )
 
    Dim Content As String = StringWriter.RunMethod("toString", Null)
    StringWriter.RunMethod("close", Null)
 
    Log(Content)
End Sub
 

Attachments

Last edited:
I just found this project yesterday and I am really new to it. By luck, I am able to make it works.
This template engine may not as advanced as Freemarker but it seems simple to use and small.
 
It can be used as a script engine too, so may be easy to wrap for B4X.
 
Small snippet - (not a wrap of the scriptengine) but it can be wrapped like I did for javascript-jruby-jgroovy etc
Code:
'Non-UI application (console / server application)
#Region Project Attributes 
    #CommandLineArgs:
    #MergeLibraries: True 
    #AdditionalJar: commons-lang3-3.12.0
    #AdditionalJar: velocity-engine-core-2.3
    #AdditionalJar: velocity-engine-scripting-2.3
    #AdditionalJar: slf4j-api-2.0.7
    #AdditionalJar: slf4j-simple-2.0.7
#End Region

Sub Process_Globals
    Dim engineManager As JavaObject
    Dim vtlEngine As JavaObject
    Dim vtl As JavaObject
End Sub

Sub AppStart (Args() As String)
    engineManager.InitializeNewInstance("javax.script.ScriptEngineManager",Null)
    vtl.InitializeNewInstance("org.apache.velocity.script.VelocityScriptEngineFactory",Null)
    engineManager.RunMethod("registerEngineName",Array("velocity",vtl))

    vtlEngine = engineManager.RunMethod("getEngineByName",Array("velocity"))
    
    vtlEngine.RunMethod("put",Array("world","people"))
    Log(vtlEngine.RunMethod("eval",Array As String("Hello $world")))
End Sub
 
Small snippet - (not a wrap of the scriptengine) but it can be wrapped like I did for javascript-jruby-jgroovy etc
Code:
'Non-UI application (console / server application)
#Region Project Attributes 
    #CommandLineArgs:
    #MergeLibraries: True 
    #AdditionalJar: commons-lang3-3.12.0
    #AdditionalJar: velocity-engine-core-2.3
    #AdditionalJar: velocity-engine-scripting-2.3
    #AdditionalJar: slf4j-api-2.0.7
    #AdditionalJar: slf4j-simple-2.0.7
#End Region

Sub Process_Globals
    Dim engineManager As JavaObject
    Dim vtlEngine As JavaObject
    Dim vtl As JavaObject
End Sub

Sub AppStart (Args() As String)
    engineManager.InitializeNewInstance("javax.script.ScriptEngineManager",Null)
    vtl.InitializeNewInstance("org.apache.velocity.script.VelocityScriptEngineFactory",Null)
    engineManager.RunMethod("registerEngineName",Array("velocity",vtl))

    vtlEngine = engineManager.RunMethod("getEngineByName",Array("velocity"))
    
    vtlEngine.RunMethod("put",Array("world","people"))
    Log(vtlEngine.RunMethod("eval",Array As String("Hello $world")))
End Sub
Thanks for the example. The difference I can see here is it doesn't use a template file.
I also learn that Velocity also have a Tool but I yet to understand how it works. There are a lot of potential to explore if you find time.
 
Minor change - makes the log better (without the data after the result)
Code:
    vtlEngine.RunMethod("put",Array("world","people"))
    
    vtlEngine.RunMethod("eval",Array As String($"#set($res = "Hello $world")"$))
    Dim s As String = vtlEngine.RunMethod("get",Array("res"))
    Log(s)
 
Bit of an expanded example
Code:
    Dim myList As List
    myList.Initialize
    For a = 0 To 10 
        myList.Add(a)
    Next

    Dim myScript As String ' the script
    myScript = $"
#set($myRes = 
"<ul>
#foreach( $product in $allProducts )
    <li>$product</li>
#end
</ul>")"$
    vtlEngine.RunMethod("put",Array("allProducts",myList))  ' pass the java array to the engine
    vtlEngine.RunMethod("eval",Array(myScript)) ' run the script
    Log(vtlEngine.RunMethod("get",Array("myRes"))) ' get the result
 
Bit of an expanded example
Code:
    Dim myList As List
    myList.Initialize
    For a = 0 To 10
        myList.Add(a)
    Next

    Dim myScript As String ' the script
    myScript = $"
#set($myRes =
"<ul>
#foreach( $product in $allProducts )
    <li>$product</li>
#end
</ul>")"$
    vtlEngine.RunMethod("put",Array("allProducts",myList))  ' pass the java array to the engine
    vtlEngine.RunMethod("eval",Array(myScript)) ' run the script
    Log(vtlEngine.RunMethod("get",Array("myRes"))) ' get the result
I am curious you are just started playing or you already use this scripting engine for a long time. ?
 
I only started with VTL when I saw your post today.
 
I tried their html table generation.
For some reason it dislikes B4X Classes and Types, as I found no way to call a sub in a class from the script. (I think it's the underscore causing it to ignore the method names)
If I created a java class I could then call the methods no problem. (Maybe someone else could have a look - fresh eyes and all that)

example
Code:
...
    Dim script2 As String
    script2=$"
#set($myPerson =
"<table>
    <tr>
        <th width='200px'>Name</th>
        <th>Age</th>
        <th>Address</th>
        <th>Postcode</th>
    </tr>
#foreach( $p in $peeps )
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
#end
</table>")"$   
    Dim peps As List
    peps.Initialize
   
    For t = 0 To 4
        Dim pp As JavaObject   
        pp.InitializeNewInstance("b4j.example.main.Members",Null)
        pp.RunMethod("setName",Array("Fred Bloggs" & t))
        pp.RunMethod("setAge",Array(12 + t))
        pp.RunMethod("setAddress",Array("123 the road" & t))
        pp.RunMethod("setPostcode",Array("RM10 " & t & "aa"))
        peps.Add(pp)
    Next
   
    vtlEngine.RunMethod("put",Array("peeps",peps))
    vtlEngine.RunMethod("eval",Array(script2))
    Dim mytable As String = vtlEngine.RunMethod("get",Array("myPerson"))
    Log(mytable)

    ' next line for checking in Edge to see the table
    'File.WriteString("c:/temp","vtlText.html",mytable)
End Sub


#if java
public static class Members{
    private String  name;
    private Integer age;
    private String  address;
    private String  postcode;
   
    public Members(){}
   
    public void setName(String n){
        this.name = n;
    }
    public void setAge(Integer a){
        this.age = a;
    }
    public void setAddress(String addy){
        this.address = addy;
    }
    public void setPostcode(String p){
        this.postcode = p;
    }
   
    public String getName(){
        return this.name;
    }
    public Integer getAge(){
        return this.age;
    }
    public String getAddress(){
        return this.address;
    }
    public String getPostcode(){
        return this.postcode;
    }
}
#End If
 
I tried their html table generation.
For some reason it dislikes B4X Classes and Types, as I found no way to call a sub in a class from the script. (I think it's the underscore causing it to ignore the method names)
If I created a java class I could then call the methods no problem. (Maybe someone else could have a look - fresh eyes and all that)

example
Code:
...
    Dim script2 As String
    script2=$"
#set($myPerson =
"<table>
    <tr>
        <th width='200px'>Name</th>
        <th>Age</th>
        <th>Address</th>
        <th>Postcode</th>
    </tr>
#foreach( $p in $peeps )
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
#end
</table>")"$  
    Dim peps As List
    peps.Initialize
  
    For t = 0 To 4
        Dim pp As JavaObject  
        pp.InitializeNewInstance("b4j.example.main.Members",Null)
        pp.RunMethod("setName",Array("Fred Bloggs" & t))
        pp.RunMethod("setAge",Array(12 + t))
        pp.RunMethod("setAddress",Array("123 the road" & t))
        pp.RunMethod("setPostcode",Array("RM10 " & t & "aa"))
        peps.Add(pp)
    Next
  
    vtlEngine.RunMethod("put",Array("peeps",peps))
    vtlEngine.RunMethod("eval",Array(script2))
    Dim mytable As String = vtlEngine.RunMethod("get",Array("myPerson"))
    Log(mytable)

    ' next line for checking in Edge to see the table
    'File.WriteString("c:/temp","vtlText.html",mytable)
End Sub


#if java
public static class Members{
    private String  name;
    private Integer age;
    private String  address;
    private String  postcode;
  
    public Members(){}
  
    public void setName(String n){
        this.name = n;
    }
    public void setAge(Integer a){
        this.age = a;
    }
    public void setAddress(String addy){
        this.address = addy;
    }
    public void setPostcode(String p){
        this.postcode = p;
    }
  
    public String getName(){
        return this.name;
    }
    public Integer getAge(){
        return this.age;
    }
    public String getAddress(){
        return this.address;
    }
    public String getPostcode(){
        return this.postcode;
    }
}
#End If
wow, I think you already fall in love with this library. You have written so much tests. ?
 
No I haven't to be honest. (I rarely use any B4X code as I don't need cross platform code)
 
I tested with type, it doesn't work.

B4X:
Type Members (Name As String, Age As Int, Address As String, Postcode As String)

B4X:
For t = 0 To 4
    Dim pp2 As Members
    pp2.Initialize
    pp2.Name = "Aeric " & t
    pp2.Age = 12 + t
    pp2.Address = "123 the road " & t
    pp2.Postcode = "RM10 " & t & "aa"
    peps.Add(pp2)
Next

Code:
Waiting for debugger to connect...
Program started.
<table>
    <tr>
        <th width='200px'>Name</th>
        <th>Age</th>
        <th>Address</th>
        <th>Postcode</th>
    </tr>
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
</table>
Program terminated (StartMessageLoop was not called).

Edit: Using a standard class also not working either.
 
Last edited:
Have you tried using B4X Maps?
Maps don't work too.

Edit:
It works for Map!

B4X:
For t = 0 To 4
    Dim pp3 As Map
    pp3.Initialize
    pp3.Put("Name", "Aeric " & t)
    pp3.Put("Age", 12 + t)
    pp3.Put("Address", "123 the road " & t)
    pp3.Put("Postcode", "RM10 " & t & "aa")
    peps.Add(pp3)
Next

HTML:
#foreach( $p in $peeps )
        <tr>
            <td>$p.get('Name')</td>
            <td>$p.get('Age')</td>
            <td>$p.get('Address')</td>
            <td>$p.get('Postcode')</td>
        </tr>
#end

edit: strange that it doesn't like double quotes but works with single quote.
 
Last edited:
Another example using evaluate method without loading template file into the engine.

B4X:
Public Sub Test5
    Dim Velocity As JavaObject
    Velocity.InitializeStatic("org.apache.velocity.app.Velocity")
    
    'Dim VelocityEngine As JavaObject
    'VelocityEngine.InitializeNewInstance("org.apache.velocity.app.VelocityEngine", Null)
    
    Velocity.RunMethod("init", Null)
    
    Dim Context As JavaObject
    Context.InitializeNewInstance("org.apache.velocity.VelocityContext", Null)
    Context.RunMethod("put", Array("name", "Velocity"))
    
    Dim Content As String = File.ReadString(File.DirAssets, "index.vm")
    Content = $"#set($content = "${Content}")
    #evaluate($content)"$

    Dim StringWriter As JavaObject
    StringWriter.InitializeNewInstance("java.io.StringWriter", Null)
    Velocity.RunMethod("evaluate", Array(Context, StringWriter, "", Content))
    Content = StringWriter.RunMethod("toString", Null)
    StringWriter.RunMethod("close", Null)
    Log(Content)
End Sub

There are 2 ways to use the Velocity Engine.

Singleton Model:
B4X:
Dim Velocity As JavaObject
Velocity.InitializeStatic("org.apache.velocity.app.Velocity")

Separate Instance:
B4X:
Dim VelocityEngine As JavaObject
VelocityEngine.InitializeNewInstance("org.apache.velocity.app.VelocityEngine", Null)
 
Last edited:
How did you come out with the Scripting Engine?
I found it by a search as the examples on apache velocity pages were not too advanced.

Have a look here - it has really good examples (at bottom of page for more) Good Info
 
Last edited:
Back
Top