Skip to content

Commit

Permalink
rtemplate binary cache for static strings and optimized encoding of v…
Browse files Browse the repository at this point in the history
…ariables
  • Loading branch information
rizsi committed Aug 16, 2022
1 parent d17a85e commit d7e781c
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 40 deletions.
4 changes: 2 additions & 2 deletions rtemplate.conf
@@ -1,9 +1,9 @@
javaDir=src
templateDir=template
autoTab=true
jTemplatePre=writer.write("
jTemplatePre=write("
jTemplatePost=");
jOutPre=writer.write(
jOutPre=writeValue(
jOutPost=);
tPre0=#H
tPost0=#
Expand Down
@@ -1,7 +1,10 @@
package com.jeroenreijn.examples.view;

import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
Expand All @@ -24,10 +27,10 @@ protected void renderMergedTemplateModel(Map<String, Object> model, HttpServletR
com.jeroenreijn.examples.model.i18nLayout i18n=(com.jeroenreijn.examples.model.i18nLayout)model.get("i18n");
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
try (PrintWriter writer = response.getWriter()) {
RtemplateViewInstance vi=new RtemplateViewInstance(context, writer, presentations, i18n);
try (OutputStream os = response.getOutputStream()) {
// PrintStream ps=new PrintStream(os, false, StandardCharsets.UTF_8);
RtemplateViewInstance vi=new RtemplateViewInstance(context, os, presentations, i18n);
vi.render();
writer.flush();
}
}
}
@@ -1,7 +1,13 @@
package com.jeroenreijn.examples.view;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import org.springframework.web.servlet.support.RequestContext;

Expand All @@ -14,96 +20,118 @@
* each query. This is the object that serves a single query.
*/
public class RtemplateViewInstance {
private PrintWriter writer;
private OutputStream os;
private Iterable<Presentation> presentations;
private i18nLayout i18n;
private RequestContext context;
private CharsetEncoder encoder=StandardCharsets.UTF_8.newEncoder();
CharBuffer cb=CharBuffer.wrap(new char[4096]);
byte[] arr=new byte[4096*5];
ByteBuffer bb=ByteBuffer.wrap(arr);
static private StringCache sc=new StringCache();
/**
* Create object that servers a single query and stores all data sources.
* @param context
* @param writer
* @param presentations
* @param i18n
*/
public RtemplateViewInstance(RequestContext context, PrintWriter writer, Iterable<Presentation> presentations, i18nLayout i18n) {
public RtemplateViewInstance(RequestContext context, OutputStream os, Iterable<Presentation> presentations, i18nLayout i18n) {
this.context=context;
this.writer=writer;
this.os=os;
this.presentations=presentations;
this.i18n=i18n;
}
private void write(String s) throws IOException
{
sc.write(os, s);
}
private void writeValue(String s) throws IOException
{
os.write(s.getBytes(StandardCharsets.UTF_8));
}
/**
* Execute rendering of the page into the stored writer object.
*/
public void render() {
writer.write("<!DOCTYPE html>\n<html>\n");
public void render() throws IOException {
write("<!DOCTYPE html>\n<html>\n");
head();
writer.write(" <body>\n <div class=\"container\">\n <div class=\"pb-2 mt-4 mb-3 border-bottom\">\n <h1>");
write(" <body>\n <div class=\"container\">\n <div class=\"pb-2 mt-4 mb-3 border-bottom\">\n <h1>");
writeHtml(i18n.message("example.title"));
writer.write(" - Rtemplate</h1>\n </div>\n");
write(" - Rtemplate</h1>\n </div>\n");
for(Presentation p: presentations)
{
presentation(p);
}
writer.write(" </div>\n");
write(" </div>\n");
scripts();
writer.write(" </body>\n</html>\t\n");
write(" </body>\n</html>\t\n");
}
/**
* Write a string through HTML escaping to the writer.
* For the sake of simplicity no library is used. In real world usage this would
* be backed by an escaping library.
* @param message
*/
private void writeHtml(String message) {
private void writeHtml(String message) throws IOException {
cb.clear();
if(message.length()>2048)
{
throw new RuntimeException("Too long");
}
for(int i=0;i<message.length();++i)
{
char ch=message.charAt(i);
switch (ch) {
case '<':
writer.write("&lt;");
cb.append("&lt;");
break;
case '>':
writer.write("&gt;");
cb.append("&gt;");
break;
case '&':
writer.write("&amp;");
cb.append("&amp;");
break;
case '"':
writer.write("&quot;");
cb.append("&quot;");
break;
default:
writer.write(ch);
cb.append(ch);
break;
}
}
bb.clear();
cb.flip();
encoder.encode(cb, bb, true);
os.write(arr, 0, bb.position());
}
/**
* Render a single presentation.
* @param p
*/
private void presentation(Presentation p) {
writer.write("<div class=\"card mb-3 shadow-sm rounded\">\n <div class=\"card-header\">\n <h5 class=\"card-title\">");
writer.write(p.getTitle());
writer.write(" - ");
writer.write(p.getSpeakerName());
writer.write("</h5>\n </div>\n <div class=\"card-body\">\n ");
writer.write(p.getSummary());
writer.write("\n </div>\n</div>\n");
private void presentation(Presentation p) throws IOException {
write("<div class=\"card mb-3 shadow-sm rounded\">\n <div class=\"card-header\">\n <h5 class=\"card-title\">");
writeHtml(p.getTitle());
write(" - ");
writeHtml(p.getSpeakerName());
write("</h5>\n </div>\n <div class=\"card-body\">\n ");
writeValue(p.getSummary());
write("\n </div>\n</div>\n");
}
/** Render the head part of the HTML page. */
private void head() {
writer.write("<head>\n <meta charset=\"UTF-8\"/>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge\"/>\n <title>");
writer.write(i18n.message("example.title"));
writer.write(" - Rtemplate</title>\n <link rel=\"stylesheet\" href=\"");
writer.write(context.getContextPath());
writer.write("/webjars/bootstrap/4.3.1/css/bootstrap.min.css\"/>\n</head>\n");
private void head() throws IOException {
write("<head>\n <meta charset=\"UTF-8\"/>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge\"/>\n <title>");
writeHtml(i18n.message("example.title"));
write(" - Rtemplate</title>\n <link rel=\"stylesheet\" href=\"");
writeValue(context.getContextPath());
write("/webjars/bootstrap/4.3.1/css/bootstrap.min.css\"/>\n</head>\n");
}
/** Render the scripts part of the HTML page. */
private void scripts() {
writer.write("<script src=\"");
writer.write(context.getContextPath());
writer.write("/webjars/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"");
writer.write(context.getContextPath());
writer.write("/webjars/bootstrap/4.3.1/js/bootstrap.min.js\"></script>\n");
private void scripts() throws IOException {
write("<script src=\"");
writeValue(context.getContextPath());
write("/webjars/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"");
writeValue(context.getContextPath());
write("/webjars/bootstrap/4.3.1/js/bootstrap.min.js\"></script>\n");
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/jeroenreijn/examples/view/StringCache.java
@@ -0,0 +1,21 @@
package com.jeroenreijn.examples.view;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class StringCache {
private Map<String, byte[]> stringRendered=new HashMap<>();

public void write(OutputStream os, String s) throws IOException {
byte[] data=stringRendered.get(s);
if(data==null)
{
data=s.getBytes(StandardCharsets.UTF_8);
stringRendered.put(s, data);
}
os.write(data);
}
}

0 comments on commit d7e781c

Please sign in to comment.