Class FreeMarkerExtension
- All Implemented Interfaces:
ReportFactoryExtension
An extension to use the Apache FreeMarker template library as a pre-processor to generate the HTML/XML parsed by BFO Publisher.
This extension is invoked by including a <?freemarker href="template.ftl"?>
processing
instruction at the start of the document - the href
attribute refers
to the template, so the document being parsed is an XML representation of the data.
The example from the FreeMarker documentation
could be created as
<?freemarker href="path/to/template.ftl"?>
<data>
<user>Big Joe</user>
<latestProduct>
<url>products/greenmouse.html</user>
<name>green mouse</user>
</latestProduct>
</data>
The processing instruction doesn't have to be added to the XML; it can be added via the API:
Report report = reportFactory.createReport();
ProcessingInstruction pi = new ProcessingInstruction().setType("freemarker").put("href", "path/to/template.ftl"");
report.getProcessingInstructions().add(pi);
report.load(new File("data.xml"));
report.parse();
PDF pdf = output.getPDF();
pdf.render(new FileOutputStream("out.pdf"));
XML isn't the only way to represent data; the technique of manually
adding a processing instruction can also be applied to data stored as JSON, CBOR or in a
FreeMarker TemplateModel
. For JSON or CBOR it is parsed the same way as XML;
any stream, file or URL with a media-type of application/json
or application/cbor
will be
passed through FreeMarker, so long as the freemarker
processing-instruction has been added via the API. So to convert JSON, the code sample directly
above would be modified only to change the file to JSON, eg report.load(new File("data.json"));
To load from a TemplateModel
, just pass it in to the Report.load()
method:
Report report = reportFactory.createReport();
ProcessingInstruction pi = new ProcessingInstruction("freemarker", "href=\"path/to/template.ftl\"");
report.getProcessingInstructions().add(pi);
Map<String,String> data = loadDataModel(); // Your method
FreeMarkerExtension ext = reportFactory.getReportFactoryExtension(FreeMarkerExtension.class);
Configuration cfg = ext.getConfiguration();
report.load(cfg.getObjectWrapper().wrap(data));
report.parse();
PDF pdf = output.getPDF();
pdf.render(new FileOutputStream("out.pdf"));
Template format and relative paths
By default the output of any Template is assumed to be HTML
(even when the file containing the data is XML, as shown in the first example above).
If the template outputs XML instead,
set the type
attribute on the processing instruction to text/xml
. In XML:
<?freemarker type="text/xml" href="path/to/template.ftl?>
or in Java, either of these
new ProcessingInstruction("freemarker", "type=\"text/html\" href=\"path/to/template.ftl\"");
new ProcessingInstruction().setType("freemarker").put("type", "text/html").put("href", "path/to/template.ftl");
Relative paths in the template will be resolved relative to the template file, not relative to the input.
Configuration
Any properties in the map returned from Report.getEnvironment()
and
ReportFactory.getEnvironment()
that begin with freemarker.
will be
passed to the Configuration
(for ReportFactory.getEnvironment()
) or
Environment
(for Report.getEnvironment()
), minus the freemarker.
prefix.
Security
BFO Publisher has no control over Freemarker Template processing, and as Freemarker currently has
no "runaway" checks, a careless or malicious template could easily create an OutOfMemoryError
.
To mitigate this, FreeMarker Templates must always be loaded from a trusted URL -
see URLConnectionFactory.isTrusted(org.faceless.publisher.type.URL2)
. By default
this means the resolved Template URL must have a scheme of classpath
, file
or jar
, or if loading from a MemoryURLConnectionFactory
the
AbstractBlob.isTrusted()
method must return true
.
- Since:
- 1.3
- See Also:
-
MustacheExtension
-
Constructor Summary
ConstructorDescriptionCreate a new FreeMarkerExtensionFreeMarkerExtension
(Configuration conf) Create a new FreeMarkerExtension -
Method Summary
Modifier and TypeMethodDescriptionvoid
configure
(Json json) Configure the FreeMarker extension.Configuration
Return the FreeMarker Configuration.boolean
Return the value set bysetThreads(boolean)
boolean
Attempt to load the specified object into the specified Report.void
Notify this object it has beem added to a Report.void
register
(ReportFactory factory) Notify this object it has beem added to a ReportFactory.void
setConfiguration
(Configuration conf) Set the FreeMarker Configuration.void
setThreads
(boolean threads) Set whether to use two threads when converting the Template.void
unregister
(ReportFactory factory) Notify this object it has been removed from a ReportFactory.Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
Methods inherited from interface org.faceless.publisher.ext.ReportFactoryExtension
unregister
-
Constructor Details
-
FreeMarkerExtension
public FreeMarkerExtension()Create a new FreeMarkerExtension -
FreeMarkerExtension
public FreeMarkerExtension(Configuration conf) Create a new FreeMarkerExtension- Parameters:
conf
- the FreeMarker Configuration to pass tosetConfiguration(Configuration)
-
-
Method Details
-
setConfiguration
public void setConfiguration(Configuration conf) Set the FreeMarker Configuration. If this is not specified by the time the first FreeMarker document is converted, a default Configuration will be used. This method can only be called once.- Parameters:
conf
- the FreeMarker Configuration to pass tosetConfiguration(Configuration)
-
getConfiguration
public Configuration getConfiguration()Return the FreeMarker Configuration. If one has not been set previously, this will callsetConfiguration()
with a default configuration. -
configure
public void configure(Json json) Configure the FreeMarker extension.
Currently only the the boolean parameter
threads
can be set to callsetThreads(boolean)
- Specified by:
configure
in interfaceReportFactoryExtension
- Parameters:
json
- the json configuration
-
load
Attempt to load the specified object into the specified Report. Ifobject
is aTemplateModel
. this method will use theTemplate
specified by thefreemarker
ProcessingInstruction
to load the input. Otherwise it will return false.- Specified by:
load
in interfaceReportFactoryExtension
- Parameters:
object
- the object to load, which could be aTemplateModel
report
- the report- Returns:
- true if this object can be loaded by this extension, false otherwise.
- Throws:
IOException
-
unregister
Description copied from interface:ReportFactoryExtension
Notify this object it has been removed from a ReportFactory. Will be called when this object is removed from the list returned byReportFactory.getReportFactoryExtensions()
. The default implementation is a no-op.- Specified by:
unregister
in interfaceReportFactoryExtension
-
register
Description copied from interface:ReportFactoryExtension
Notify this object it has beem added to a ReportFactory. Will be called when this object is added to the list returned byReportFactory.getReportFactoryExtensions()
. The default implementation is a no-op.- Specified by:
register
in interfaceReportFactoryExtension
-
register
Description copied from interface:ReportFactoryExtension
Notify this object it has beem added to a Report. Will be called when this object is added to the list returned byReportFactory.getReportFactoryExtensions()
. The default implementation is a no-op.- Specified by:
register
in interfaceReportFactoryExtension
-
setThreads
public void setThreads(boolean threads) Set whether to use two threads when converting the Template.By default only a single thread is used; the output from the template+object is written to a String, and after it completes that String is fed into the XML parser to create the Report.
Setting
Depending on the implementation of the template library, using two threads should prevent the entire XML document from being stored in memory, which will be beneficial when small or mid-size templates are combined with large models to create extremely large output.threads
totrue
will convert the template+object in a new Thread, with the output from that Thread piped into the main Thread to convert to a Report.- Parameters:
threads
- whether to use two Threads when applying the Template
-
isThreads
public boolean isThreads()Return the value set bysetThreads(boolean)
-