diff --git a/pom.xml b/pom.xml
index 825a5b2c..12afd42c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
fr.wseduc
web-utils
- 3.2-SNAPSHOT
+ 3.2-develop-b2school-SNAPSHOT
diff --git a/src/main/java/fr/wseduc/webutils/http/ProcessTemplateContext.java b/src/main/java/fr/wseduc/webutils/http/ProcessTemplateContext.java
new file mode 100644
index 00000000..2ce1b3e1
--- /dev/null
+++ b/src/main/java/fr/wseduc/webutils/http/ProcessTemplateContext.java
@@ -0,0 +1,117 @@
+package fr.wseduc.webutils.http;
+
+import com.samskivert.mustache.Mustache;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.json.JsonObject;
+
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Container for all parameters needed for the template processing
+ */
+public final class ProcessTemplateContext {
+
+ private final HttpServerRequest request;
+ private final JsonObject params;
+ private final String templateString;
+ private final Reader reader;
+ private final boolean escapeHtml;
+ private final String defaultValue;
+ private final Map lambdas;
+
+ private ProcessTemplateContext(HttpServerRequest request, JsonObject params, String templateString, Reader reader,
+ boolean escapeHtml, String defaultValue, Map lambdas) {
+ this.request = request;
+ this.params = params;
+ this.templateString = templateString;
+ this.reader = reader;
+ this.escapeHtml = escapeHtml;
+ this.defaultValue = defaultValue;
+ this.lambdas = new HashMap<>(lambdas);
+ }
+
+ public static class Builder {
+
+ private HttpServerRequest request;
+ private JsonObject params;
+ private String templateString;
+ private Reader reader;
+ private boolean escapeHtml;
+ private String defaultValue;
+ private final Map lambdas = new HashMap<>();
+
+ public Builder request(HttpServerRequest request) {
+ this.request = request;
+ return this;
+ }
+
+ public Builder escapeHtml(boolean escapeHtml) {
+ this.escapeHtml = escapeHtml;
+ return this;
+ }
+
+ public Builder setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ return this;
+ }
+
+ public Builder params(JsonObject params) {
+ this.params = params;
+ return this;
+ }
+
+ public Builder templateString(String templateString) {
+ this.templateString = templateString;
+ return this;
+ }
+
+ public Builder reader(Reader reader) {
+ this.reader = reader;
+ return this;
+ }
+
+ public Builder lambdas(Map lambdas) {
+ this.lambdas.putAll(lambdas);
+ return this;
+ }
+
+ public HttpServerRequest request() {
+ return request;
+ }
+
+ public ProcessTemplateContext build() {
+ return new ProcessTemplateContext(request, params, templateString, reader, escapeHtml, defaultValue, lambdas);
+ }
+
+ }
+
+ public HttpServerRequest request() {
+ return request;
+ }
+
+ public JsonObject params() {
+ return params;
+ }
+
+ public String templateString() {
+ return templateString;
+ }
+
+ public Reader reader() {
+ return reader;
+ }
+
+ public Map lambdas() {
+ return lambdas;
+ }
+
+ public boolean escapeHtml() {
+ return escapeHtml;
+ }
+
+ public String defaultValue() {
+ return defaultValue;
+ }
+}
diff --git a/src/main/java/fr/wseduc/webutils/http/Renders.java b/src/main/java/fr/wseduc/webutils/http/Renders.java
index 520f973d..1ef598f9 100644
--- a/src/main/java/fr/wseduc/webutils/http/Renders.java
+++ b/src/main/java/fr/wseduc/webutils/http/Renders.java
@@ -20,8 +20,11 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import com.samskivert.mustache.Mustache;
import fr.wseduc.webutils.template.TemplateProcessor;
import fr.wseduc.webutils.template.FileTemplateProcessor;
import fr.wseduc.webutils.template.lambdas.FormatBirthDateLambda;
@@ -30,6 +33,7 @@
import fr.wseduc.webutils.template.lambdas.LocaleDateLambda;
import fr.wseduc.webutils.template.lambdas.ModsLambda;
import fr.wseduc.webutils.template.lambdas.StaticLambda;
+import io.micrometer.common.util.StringUtils;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
@@ -47,6 +51,12 @@
import static fr.wseduc.webutils.Utils.isNotEmpty;
+/**
+ * Method with @deprecated, relative to template processing are NOT thread safe and should not be used. Alternative method not deprecated was developed.
+ * Lambda for template processing dependent of the user context (request, host, etc..) must not be shared and are local variable.
+ * Static lambda that doesn't change with the context can be used
+ * Parameter for the template are either NOT thread safe. EscapeHTML, defaultValue, should be passed as local variable
+ */
public class Renders {
protected static final Logger log = LoggerFactory.getLogger(Renders.class);
@@ -57,6 +67,7 @@ public class Renders {
protected String staticHost;
protected FileTemplateProcessor templateProcessor;
protected static final List allowedHosts = new ArrayList<>();
+ private final Map staticLambdas = new HashMap<>();
public Renders(Vertx vertx, JsonObject config) {
this.config = config;
@@ -66,8 +77,10 @@ public Renders(Vertx vertx, JsonObject config) {
this.vertx = vertx;
if (vertx != null) {
this.templateProcessor = new FileTemplateProcessor(vertx, "view/", false);
- this.templateProcessor.setLambda("formatBirthDate", new FormatBirthDateLambda());
- this.templateProcessor.setLambda("modVersion", new ModsLambda(vertx));
+ staticLambdas.put("formatBirthDate", new FormatBirthDateLambda());
+ staticLambdas.put("modVersion", new ModsLambda(vertx));
+
+ staticLambdas.forEach(templateProcessor::setLambda);
}
}
@@ -84,13 +97,15 @@ protected void init(Vertx vertx, JsonObject config)
if (templateProcessor == null && vertx != null) {
this.templateProcessor = new FileTemplateProcessor(vertx, "view/", false);
- this.templateProcessor.setLambda("formatBirthDate", new FormatBirthDateLambda());
- this.templateProcessor.setLambda("modVersion", new ModsLambda(vertx));
+ staticLambdas.put("formatBirthDate", new FormatBirthDateLambda());
+ staticLambdas.put("modVersion", new ModsLambda(vertx));
+
+ staticLambdas.forEach(templateProcessor::setLambda);
}
}
- protected void setLambdaTemplateRequest(final HttpServerRequest request)
- {
+ @Deprecated
+ protected void setLambdaTemplateRequest(final HttpServerRequest request) {
String host = Renders.getHost(request);
if(host == null) // This can happen for forged requests
host = "";
@@ -104,6 +119,88 @@ protected void setLambdaTemplateRequest(final HttpServerRequest request)
this.templateProcessor.setLambda("datetime", new LocaleDateLambda(I18n.acceptLanguage(request)));
}
+ /**
+ * Create lambda from request for mustache. These lambdas should be used with TemplateProcessorContext and method
+ * that use it in order to avoid race condition on template processing
+ * @param request HttpRequest of the user to set various dependent request lambda (host, domain, language, etc..)
+ * @return Map with request dependent lambda
+ */
+ protected Map getLambdasFromRequest(final HttpServerRequest request) {
+ Map lambdas = new HashMap<>();
+
+ String host = Renders.getHost(request);
+ if(host == null) // This can happen for forged requests
+ host = "";
+ String sttcHost = this.staticHost != null ? this.staticHost : host;
+
+
+ lambdas.put("i18n",
+ new I18nLambda(I18n.acceptLanguage(request), host, I18n.getTheme(request)));
+ lambdas.put("static",
+ new StaticLambda(config.getBoolean("ssl", sttcHost.startsWith("https")), sttcHost, this.pathPrefix + "/public"));
+ lambdas.put("infra",
+ new InfraLambda(config.getBoolean("ssl", sttcHost.startsWith("https")), sttcHost, "/infra/public", request.headers().get("X-Forwarded-For") == null));
+ lambdas.put("datetime", new LocaleDateLambda(I18n.acceptLanguage(request)));
+ return lambdas;
+ }
+
+ public void renderTemplateView(HttpServerRequest request) {
+ renderTemplateView(request, new JsonObject(), new HashMap<>());
+ }
+
+ public void renderTemplateView(HttpServerRequest request, Map lambdas) {
+ renderTemplateView(request, new JsonObject(), lambdas);
+ }
+
+ public void renderTemplateView(HttpServerRequest request, JsonObject params) {
+ renderTemplateView(request, params, new HashMap<>());
+ }
+
+ public void renderTemplateView(HttpServerRequest request, JsonObject params, Map lambdas) {
+ renderTemplateView(request, params, null, null, 200, lambdas);
+ }
+
+ public void renderTemplateView(HttpServerRequest request, JsonObject params, String resourceName,
+ Reader r, Map lambdas) {
+ renderTemplateView(request, params, resourceName, r, 200, lambdas);
+ }
+
+ public void renderTemplateView(final HttpServerRequest request, JsonObject params,
+ String resourceName, Reader r, final int status, Map lambdas) {
+
+ Map mergeLambdas = getLambdasFromRequest(request);
+ mergeLambdas.putAll(lambdas);
+ mergeLambdas.putAll(staticLambdas);
+
+ ProcessTemplateContext.Builder context = new ProcessTemplateContext.Builder()
+ .escapeHtml(true)
+ .reader(r)
+ .lambdas( mergeLambdas)
+ .request(request);
+
+ context.templateString(genTemplateName(resourceName, request));
+
+ templateProcessor.processTemplate(context.build(), writer -> {
+ if (writer != null) {
+ request.response().putHeader("content-type", "text/html; charset=utf-8");
+ request.response().setStatusCode(status);
+ if (hookRenderProcess != null) {
+ executeHandlersHookRender(request, new Handler() {
+ @Override
+ public void handle(Void v) {
+ request.response().end(writer.toString());
+ }
+ });
+ } else {
+ request.response().end(writer.toString());
+ }
+ } else {
+ renderError(request);
+ }
+ });
+ }
+
+ @Deprecated
public void renderView(HttpServerRequest request) {
renderView(request, new JsonObject());
}
@@ -112,14 +209,17 @@ public void renderView(HttpServerRequest request) {
* Render a Mustache template : see http://mustache.github.com/mustache.5.html
* TODO : isolate scope management
*/
+ @Deprecated
public void renderView(HttpServerRequest request, JsonObject params) {
renderView(request, params, null, null, 200);
}
+ @Deprecated
public void renderView(HttpServerRequest request, JsonObject params, String resourceName, Reader r) {
renderView(request, params, resourceName, r, 200);
}
+ @Deprecated
public void renderView(final HttpServerRequest request, JsonObject params,
String resourceName, Reader r, final int status) {
processTemplate(request, params, resourceName, r, new Handler() {
@@ -161,12 +261,14 @@ public void handle(Void v) {
handlers[0].handle(null);
}
+ @Deprecated
public void processTemplate(HttpServerRequest request, String template, JsonObject params, final Handler handler)
{
this.setLambdaTemplateRequest(request);
this.templateProcessor.escapeHTML(true).processTemplate(this.genTemplateName(template, request), params, handler);
}
+ @Deprecated
public void processTemplate(final HttpServerRequest request, JsonObject p, String resourceName, Reader r, final Handler handler)
{
this.setLambdaTemplateRequest(request);
@@ -174,24 +276,33 @@ public void processTemplate(final HttpServerRequest request, JsonObject p, Strin
this.templateProcessor.processTemplate(this.genTemplateName(resourceName, request), p, r, handler);
}
+ @Deprecated
public void processTemplate(final HttpServerRequest request, JsonObject p, String resourceName, boolean escapeHTML, final Handler handler)
{
this.setLambdaTemplateRequest(request);
this.templateProcessor.escapeHTML(escapeHTML).processTemplate(this.genTemplateName(resourceName, request), p, handler);
}
- private String genTemplateName(final String resourceName, final HttpServerRequest request)
- {
- if (resourceName != null && !resourceName.trim().isEmpty())
+ public void processTemplateWithLambdas(ProcessTemplateContext.Builder context, Handler handler) {
+ context.lambdas(getLambdasFromRequest(context.request()));
+ this.templateProcessor.processTemplate(context.build(), handler);
+ }
+
+ public void processTemplateWithLambdas(ProcessTemplateContext.Builder context, String resourceName, Handler handler) {
+ context.lambdas(getLambdasFromRequest(context.request()));
+ context.templateString(genTemplateName(resourceName, context.request()));
+ this.templateProcessor.processTemplate(context.build(), handler);
+ }
+
+ private String genTemplateName(final String resourceName, final HttpServerRequest request) {
+ if (!StringUtils.isEmpty(resourceName)) {
return resourceName;
- else
- {
- String template = request.path().substring(pathPrefix.length());
- if (template.trim().isEmpty()) {
- template = pathPrefix.substring(1);
- }
- return template + ".html";
}
+ String template = request.path().substring(pathPrefix.length());
+ if (template.trim().isEmpty()) {
+ template = pathPrefix.substring(1);
+ }
+ return template + ".html";
}
public static void ok(HttpServerRequest request) {
diff --git a/src/main/java/fr/wseduc/webutils/security/SecuredAction.java b/src/main/java/fr/wseduc/webutils/security/SecuredAction.java
index a7ecedb6..ca13383c 100644
--- a/src/main/java/fr/wseduc/webutils/security/SecuredAction.java
+++ b/src/main/java/fr/wseduc/webutils/security/SecuredAction.java
@@ -30,6 +30,14 @@ public SecuredAction(String qualifiedName, String displayName, String type, Stri
this.right = right;
}
+ public SecuredAction(String qualifiedName, String displayName, String type) {
+ this.name = qualifiedName;
+ this.displayName = displayName;
+ this.type = type;
+ this.right = qualifiedName;
+ }
+
+
public String getName() {
return name;
}
diff --git a/src/main/java/fr/wseduc/webutils/template/FileTemplateProcessor.java b/src/main/java/fr/wseduc/webutils/template/FileTemplateProcessor.java
index 9bd2d871..b953a67c 100644
--- a/src/main/java/fr/wseduc/webutils/template/FileTemplateProcessor.java
+++ b/src/main/java/fr/wseduc/webutils/template/FileTemplateProcessor.java
@@ -29,6 +29,7 @@
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import fr.wseduc.webutils.collections.JsonUtils;
+import fr.wseduc.webutils.http.ProcessTemplateContext;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
@@ -74,6 +75,7 @@ public void clearCache()
// ============================================= TEMPLATE PROCESSING ============================================
+ @Deprecated
public void processTemplate(String resourceName, JsonObject params, Reader r, final Handler handler)
{
if(r != null)
@@ -83,38 +85,50 @@ public void processTemplate(String resourceName, JsonObject params, Reader r, fi
}
@Override
+ @Deprecated
protected void getTemplate(String resourceName, final Handler handler)
{
String path = this.templateFolder + resourceName;
+ final String p = absolutePath(path);
+ handleGetTemplate(p, handler);
+ }
+
+ @Override
+ public void processTemplate(ProcessTemplateContext context, final Handler handler) {
+ if(context.reader() != null) {
+ super.getTemplate(context, t -> processTemplate(context, t , handler));
+ } else {
+ super.processTemplate(context, handler);
+ }
+ }
+
+ @Override
+ protected void getTemplate(ProcessTemplateContext context, final Handler handler) {
+ String path = this.templateFolder + context.templateString();
final String p = absolutePath(path);
- if (this.useCache == true)
- {
+ handleGetTemplate(p, handler);
+ }
+
+ private void handleGetTemplate(String p, Handler handler) {
+ if (this.useCache) {
Template cacheEntry = cache.get(p);
- if(cacheEntry != null)
- {
+ if(cacheEntry != null) {
handler.handle(cacheEntry);
return;
}
}
- this.vertx.fileSystem().readFile(p, new Handler>()
- {
- @Override
- public void handle(AsyncResult ar)
- {
- if (ar.succeeded())
- {
- Template template = compiler.compile(ar.result().toString("UTF-8"));
-
- if(useCache == true)
- cache.put(p, template);
-
- handler.handle(template);
+ this.vertx.fileSystem().readFile(p, ar -> {
+ if (ar.succeeded()) {
+ Template template = compiler.compile(ar.result().toString("UTF-8"));
+ if(useCache) {
+ cache.put(p, template);
}
- else
- handler.handle(null);
+ handler.handle(template);
+ return;
}
+ handler.handle(null);
});
}
}
\ No newline at end of file
diff --git a/src/main/java/fr/wseduc/webutils/template/TemplateProcessor.java b/src/main/java/fr/wseduc/webutils/template/TemplateProcessor.java
index 8f5176f2..36fd97de 100644
--- a/src/main/java/fr/wseduc/webutils/template/TemplateProcessor.java
+++ b/src/main/java/fr/wseduc/webutils/template/TemplateProcessor.java
@@ -21,55 +21,49 @@
import java.io.Writer;
import java.io.StringWriter;
-import java.io.Reader;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import fr.wseduc.webutils.collections.JsonUtils;
-import io.vertx.core.AsyncResult;
+import fr.wseduc.webutils.http.ProcessTemplateContext;
import io.vertx.core.Handler;
-import io.vertx.core.Vertx;
-import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
-import static fr.wseduc.webutils.data.FileResolver.absolutePath;
-
public class TemplateProcessor
{
protected static final Logger log = LoggerFactory.getLogger(TemplateProcessor.class);
protected Mustache.Compiler compiler = Mustache.compiler().defaultValue("");
- private Map templateLambdas = new ConcurrentHashMap();
-
- public TemplateProcessor()
- {
- }
+ private Map templateLambdas = new ConcurrentHashMap<>();
// =========================================== COMPILER CONFIGURATION ===========================================
+ @Deprecated
public TemplateProcessor setLambda(String identifier, Mustache.Lambda lambda)
{
this.templateLambdas.put(identifier, lambda);
return this;
}
+ @Deprecated
public TemplateProcessor clearLambda(String identifier)
{
this.templateLambdas.remove(identifier);
return this;
}
+ @Deprecated
public TemplateProcessor defaultValue(String defaultValue)
{
this.compiler = this.compiler.defaultValue(defaultValue);
return this;
}
+ @Deprecated
public TemplateProcessor escapeHTML(boolean enableHTMLEscaping)
{
this.compiler = this.compiler.escapeHTML(enableHTMLEscaping);
@@ -79,66 +73,82 @@ public TemplateProcessor escapeHTML(boolean enableHTMLEscaping)
// ============================================= TEMPLATE PROCESSING ============================================
+ @Deprecated
public void processTemplate(String templateString, JsonObject params, final Handler handler)
{
- this.processTemplateToWriter(templateString, params, new Handler()
- {
- @Override
- public void handle(Writer w)
- {
- handler.handle(w == null ? null : w.toString());
- }
- });
+ this.processTemplateToWriter(templateString, params, w -> handler.handle(w == null ? null : w.toString()));
}
+ @Deprecated
public void processTemplateToWriter(String templateString, JsonObject params, final Handler handler)
{
- this.getTemplate(templateString, new Handler()
- {
- @Override
- public void handle(Template t)
- {
- processTemplate(t, params, handler);
- }
- });
+ this.getTemplate(templateString, t -> processTemplate(t, params, handler));
}
+ @Deprecated
protected void processTemplate(Template t, JsonObject params, final Handler handler)
{
final JsonObject ctxParams = (params == null) ? new JsonObject() : params.copy();
final Map ctx = JsonUtils.convertMap(ctxParams);
this.applyLambdas(ctx);
+ applyTemplate(t, ctx, handler);
+ }
+
+ @Deprecated
+ protected void getTemplate(String templateString, final Handler handler) {
+ handler.handle(compiler.compile(templateString));
+ }
+
+ protected void getTemplate(ProcessTemplateContext context, final Handler handler) {
+ if(context.reader() == null) {
+ handler.handle(compiler.defaultValue(context.defaultValue())
+ .escapeHTML(context.escapeHtml())
+ .compile(context.templateString()));
+ } else {
+ handler.handle(compiler.defaultValue(context.defaultValue())
+ .escapeHTML(context.escapeHtml())
+ .compile(context.reader()));
+ }
+ }
+
+
+ public void processTemplate(ProcessTemplateContext templateContext, Template template, Handler handler) {
+ final JsonObject ctxParams = (templateContext.params() == null) ? new JsonObject() : templateContext.params().copy();
+ final Map ctx = JsonUtils.convertMap(ctxParams);
+ applyLambdas(templateContext, ctx);
+ applyTemplate(template, ctx, handler);
+ }
- if (t != null)
- {
- try
- {
+ public void processTemplate(ProcessTemplateContext templateContext, Handler handler) {
+ getTemplate(templateContext, h -> processTemplate(templateContext, h, handler));
+ }
+
+
+ // ================================================ PRIVATE UTILS ===============================================
+
+
+ private void applyTemplate(Template t, Map ctx, Handler handler) {
+ if (t != null) {
+ try {
Writer writer = new StringWriter();
t.execute(ctx, writer);
handler.handle(writer);
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
log.error(e.getMessage(), e);
handler.handle(null);
}
- }
- else
+ } else {
handler.handle(null);
+ }
}
- protected void getTemplate(String templateString, final Handler handler)
- {
- handler.handle(compiler.compile(templateString));
+ private void applyLambdas(ProcessTemplateContext templateContext, Map ctx) {
+ ctx.putAll(templateContext.lambdas());
}
- // ================================================ PRIVATE UTILS ===============================================
-
- private void applyLambdas(Map context)
- {
- for(Map.Entry entry : this.templateLambdas.entrySet())
- {
- context.put(entry.getKey(), entry.getValue());
- }
+ @Deprecated
+ private void applyLambdas(Map context) {
+ context.putAll(this.templateLambdas);
}
+
}
\ No newline at end of file