Does anyone know if it is possible to get templates from different paths with velocity? After initialization Velocity refuses to change the "file.resource.loader.path".
This is my code:
public Generator(){
Properties p = new Properties();
p.setProperty("resource.loader", "file");
p.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
p.setProperty("file.resource.loader.path", "");
Velocity.init(p);
}
The templates can be located in different locations ( the user can select one with a file dialog ). So I have this code upon fetching the template out of velocity
private Template fetch (String templatePath) {
out_println("Initializing Velocity core...");
int end = templatePath.lastIndexOf(File.separator);
Properties p = new Properties();
p.setProperty("file.resource.loader.path", templatePath.substring(0, end));
Velocity.init(p);
return Velocity.getTemplate(templatePath.substring(end+1));
}
This is not working. It seems that once Velocity is initialized it can't be reset with different properties. Any suggestions on how to solve this problem?
Possible Program flow:
User selects group that needs to be filled into the template
User selects a template to use (can be located anywhere on the hdd)
User presses generate
Velocity can be used in two ways: the singleton model or the separate instance model. You are currently using the singleton model in which only one instance of the Velocity engine in the JVM is allowed.
Instead, you should use the separate instance model which allows you to create multiple instances of Velocity in the same JVM in order to support different template directories.
VelocityEngine ve = new VelocityEngine();
ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, "path/to/templates");
ve.init();
Template t = ve.getTemplate("foo.vm");
Adding to the points above:
Even if one is using non-singleton model i.e using VelocityEngine object. Multiple paths can be configured by giving comma separated values to the property.
[file.resource.loader.class=path1,path2]
In such a case velocity engine will look for template in path1 first and then in path2
Consider instead of using singleton Velocity class creating and initializing new VelocityEngine before step 3.
In my case I am using Velocity with Servlets in an Eclipse Dynamic Web Project.
I couldn't actually reset the path, but I could put a subdirectory under /WebContent folder and then organize my templates that way... and have nested subdirectories as well.
RequestDispatcher requestDispatcher =
request.getRequestDispatcher("/velocity_templates/index.vm");
This simple solution was all I needed ... didn't need to mess with velocity.properties in web.xml or setting them programmatically (in each case, neither approach worked for me unfortunately when I tried).
Note that when I do template includes with #parse(..) command, I need to use the same path prefix inside the template .vm file as I did in the example code for my servlet.
Related
I am fairly new to Freemarker and to template engine in general.
Basicly I want to be able to generate two XML files (from two different templates) using java methods to feed data to the template.
For example:
myTemplate1.ftl contain an opening and closing "res" tag with getRes() method as value (return 1 for exemple)
myTemplate2.ftl contain an opening and closing "result" tag and takes getResult() method as value (return 2 for exemple)
How can I write one class Main to process this without being specific (I do not want to write line per line, because then it would be pointless to create an engine)
Can you please help me understand how it work through an example if possible ?
Regards
EDIT with all the new informations :
#fustaki This is very frustrating. It seems I need to go step by step to understand what I'm doing. So... Here is a very dumb example of where I am :
template1.ftl
<Email>${item.getEmail()}</Email><Language>${item.getLanguage()}</Language>
FillMyTemplate1.java
public String getEmail(){ return "test#test.com" }
public String getLanguage(){ return "EN" }
I am using a property file in order to use introspection : prop.properties which contain :
template1=fr.freemarker.test.xml.FillMyTemplate1
MainTest.java
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
InputStream input = new FileInputStream("prop.properties");
prop.load(input);
Class<?> classe = Class.forName(prop.getProperty(args[0])); //where args[0] is "template1"
Configuration config=new Configuration();
config.setClassForTemplateLoading(MainTest.class, "templates");
config.setObjectWrapper(new DefaultObjectWrapper());
Template template=config.getTemplate(args[0]+".ftl");
Map<String, Object> dataModel = new HashMap<String, Object>();
Writer consoleWriter = new OutputStreamWriter(System.out);
dataModel.put("item", classe.newInstance());
template.process(dataModel, consoleWriter);
}
And the result : <Email>test#test.com</Email><Language>EN</Language>
In order to understand what you said, I need to know how a "data-provider" would look like in my case ? How can I get ride of this "item" in my map ? I understand the principle but not the technical solution...
Your Main process is in charge of producing the data (List, Map or other structured object) and templates will use the data to render your XMLs.
The tags used in the templates are independent from the data inside them.
Example where data are stored in a List that can be retrieved with a getItems() or in general is accessible from the freemarker engine.
<?xml>
<res>
<#list items as item >
<item>${item}</item>
</#list>
</res>
You can create as much templates (views) as you want using the same List items.
I hope this clarify your troubles.
here is good example of freemarker. this application generate spring mvc crud operation
using a freemarker template.
springmvcfreemarker
How can I add a attribute to the current flow file when developing an Apache NiFi cusom processor.
What I want to do is adding a new attribute (or at least update a current attribute) to the current flow file with calculated value.
Or is there an already built processor that I can use?
NiFi supports several methods of creating and updating attributes, depending on the data source you wish to use. Some general purpose processors include:
UpdateAttribute - Updates attributes on flow files using both static values and NiFi's expression language.
You can add as many properties with one processor. I recommend scanning through the Apache NiFi Expression Language Guide to get a feel for what you can do with it.
ExtractText - The Sets attribute values by applying regular expressions to the flowfile content.
ExecuteScript - ExecuteScript Runs custom script code, which can be used to update attributes however you wish.
And there are more for particular content formats, for example:
EvaluateJsonPath - for JSON
EvaluateXPath - for XML
I had a use case where I needed to load many attributes from a Java-style Properties file. This could be done with ExtractText but it required adding a property and regular expression for every property to be supported. I thought it would be nice to support whatever properties were in the file without having to configure the processor for each one.
The solution I came up with was to use the ExecuteGroovyScript processor with the following script:
def ff=session.get();
if (ff != null) {
def properties = new Properties();
def is = ff.read();
properties.load(is);
is.close();
ff.putAllAttributes(properties);
REL_SUCCESS << ff;
}
This script reads the properties from a physical file rather than from the flow file.
def ff=session.get();
if (ff != null) {
def properties = new Properties();
def propertiesFile = new File('/Users/me/mydata/foo.properties')
propertiesFile.withInputStream {
properties.load(it)
}
ff.putAllAttributes(properties);
REL_SUCCESS << ff;
}
I'm using the template engine StringTemplate for some templates (obviously).
What I want is to be able to store the templates I have in seperate files, ofcourse I can do that with simple .txt files and reading them into a String, looks a bit like this then
ST template = new ST(readTemplateFromFile("template.txt"))
private String readTemplateFromFile(String templateFile){
//read template from file
}
But what I was wondering is if there's functionality in the StringTemplate engine to do that automatically. SO that i don't have to write code that already exists.
I've read something about Group Files but I don't quite understand that,are those like Template files? Or am I completely missing something?
Yes, there is functionality available that can be used directly without providing your own file loading code.
From the ST JavaDoc:
To use templates, you create one (usually via STGroup) and then inject attributes using add(java.lang.String, java.lang.Object). To render its attacks, use render().
To follow that advice the following code can be used.
First, create a file called exampleTemplate.stg and place it on your classpath.
templateExample(param) ::= <<
This is a template with the following param: (<param>)
>>
Then, render the template by using the following code:
// Load the file
final STGroup stGroup = new STGroupFile("exampleTemplate.stg");
// Pick the correct template
final ST templateExample = stGroup.getInstanceOf("templateExample");
// Pass on values to use when rendering
templateExample.add("param", "Hello World");
// Render
final String render = templateExample.render();
// Print
System.out.println(render);
The output is:
This is a template with the following param: (Hello World)
Some additional notes:
STGroupFile is a subclass of STGroup. There are other subclasses as well that you find out more about in the JavaDoc.
In the example above the template file was placed on the classpath. This is not a requirement, files can be placed in a relative folder or a in an absolute folder as well.
The following is an example of what I want to do.
I have a bunch of files such as test1.vm:
Welcome ${name}. This is test1.
Then I have a file called defaults.vm:
#set($name = "nmore")
I want render test1.vm (and the other test files) with the variable(s) in defaults.vm without using #parse as I would have to modify all the test files.
Is there a way to do this from within the accompanying java file?
I'm not sure if you have any constraints or any other specific requirements, but if you don't have you tried to use Velocity API? Something like this:
Context context = new VelocityContext();
Template template = Velocity.getTemplate("src/main/resources/defaults.vm");
template.merge(context, NullWriter.NULL_WRITER);
StringWriter writer = new StringWriter();
Template toBeParsedTemplate = Velocity.getTemplate("src/main/resources/test1.vm");
toBeParsedTemplate.merge(context, writer);
String renderedContent = writer.getBuffer().toString();
System.out.println(renderedContent);
The idea is that you fill in the Context object with the variables generated from defaults.vm and use the same context to evaluate test1.vm.
I've tried this using Velocity 1.7 and commons-io 2.4 (for the NullWriter) seems to be working fine, but I'm not sure if this can fit into your requirement or you're looking into other alternatvies (not using Velocity API).
More info on the Context object here:
http://velocity.apache.org/engine/devel/developer-guide.html#The_Context
Hope that helps.
My macros.txt file is
<#macro macro1>
Helloworld.
</#macro>
I have another file testMacro.txt. I want to use this macro inside the file testMacro.txt.
I have tried the following
<#import "./macros.txt" as my>
<#my.macro1 />
But it does not seem to be working.
In my java file, where I am working with the template file, I have(in my MacroWorking.java)
template = new Template(null,new FileReader("testMacro.txt"),new configuration());
Exception are.
Exception in thread "main" java.lang.NullPointerException
at freemarker.core.LibraryLoad.<init>(LibraryLoad.java:82)
at freemarker.core.FMParser.Import(FMParser.java:1727)
at freemarker.core.FMParser.FreemarkerDirective(FMParser.java:2389)
at freemarker.core.FMParser.Content(FMParser.java:2618)
at freemarker.core.FMParser.OptionalBlock(FMParser.java:2786)
at freemarker.core.FMParser.Root(FMParser.java:2958)
at freemarker.template.Template.<init>(Template.java:149)
at freemarker.template.Template.<init>(Template.java:172)
at msjava.hdom.examples.DbQuery.main(MacroWorking.java:24)
Line 24 of MacroWorking.java is the one given above.
EDIT: With the same code but with my testMacro.txt having text as HELLOWORLD only, i.e. no import statement, then it works fine.
What do I do?
Thanks.
There's no problem with those templates, the problem is with the way you are using the Java API of FreeMarker.
When #import tries to resolve the ./macros.txt path, it tries to resolve it relatively to the path of the current template, but since the template was loaded from a Reader and you have passed null as template name (that's the same as the template path), it will run into an NPE situation. That's an improper error message, but it couldn't resolve that path anyway, as it has no idea where the current template came from (remember, you have only given a Reader to FreeMarker). The proper way is:
Configuration cfg = new Configuration();
cfg.setDirectoryForTemplateLoading(new File("/where/you/store/the/templates"));
Template template = cfg.getTemplate("testMacro.txt");
Now FreeMarker sets the name of the template to testMacro.txt and it also takes care of loading and caching of it. If you still need to load templates directly from a Reader, you can do it as:
Template template = new Template("testMacro.txt", new FileReader(...), cfg);
Note the non-null template name. It doesn't mater if that's real, but it will be used to resolve relative paths in that template. Also note that the template-loader must be set correctly in the Configuration, because FreeMarker works with virtual-paths.