[java-shib-shared] 01/01: IDP-2235 Abstract Template from being just Velocity
Rod Widdowson
rdw at steadingsoftware.com
Mon Feb 5 20:51:42 UTC 2024
This is an automated email from the git hooks/post-receive script.
rdw pushed a commit to branch dev/IDP-2235
in repository java-shib-shared.
View the commit online:
http://git.shibboleth.net/view/?p=java-shib-shared.git;a=commit;h=184682b149cbbbcfe7a50f97053ee5cc4ca971b5
commit 184682b149cbbbcfe7a50f97053ee5cc4ca971b5
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Mon Feb 5 20:49:46 2024 +0000
IDP-2235 Abstract Template from being just Velocity
https://shibboleth.atlassian.net/browse/IDP-2235
Very very ropy first attempt to see what the code looks like.
All annotations and javadoc missing. Strtcure roughly right (I think)
That is if you dont mind private classes within priate calsses withing classes...
---
.../net/shibboleth/shared/template/Template.java | 184 +++++++++++++++++++++
.../shared/template/TemplateContext.java | 55 ++++++
.../shibboleth/shared/template/TemplateEngine.java | 37 +++++
.../shared/template/TemplateEngineTemplate.java | 23 +++
.../shibboleth/shared/template/package-info.java | 16 ++
.../shared/velocity/VelocityTemplateEngine.java | 149 +++++++++++++++++
6 files changed, 464 insertions(+)
diff --git a/shib-support/src/main/java/net/shibboleth/shared/template/Template.java b/shib-support/src/main/java/net/shibboleth/shared/template/Template.java
new file mode 100644
index 00000000..b4af29a0
--- /dev/null
+++ b/shib-support/src/main/java/net/shibboleth/shared/template/Template.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** An Implementation independent implementation of a template.
+ *
+ * It contains several helper classes
+ */
+
+package net.shibboleth.shared.template;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.common.base.Charsets;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.ConstraintViolationException;
+import net.shibboleth.shared.primitive.StringSupport;
+
+public abstract class Template {
+
+ /** The {@link TemplateEngine} used when evaluating the template. */
+ @Nonnull private final TemplateEngine templateEngine;
+
+ /** The character encoding of the template. */
+ @Nonnull @NotEmpty private final Charset templateEncoding;
+
+ /** The escape type of the template. */
+ @Nonnull @NotEmpty private final String templateEscapeType;
+
+ /** Escaping type text.
+ * One of the values that {@link #templateEscapeType} can have. */
+ @Nonnull @NotEmpty final static String TEXT_ESCAPE = "TEXT";
+
+ /**
+ * A function that TEXT templates can use to escape the
+ */
+ @Nullable private Function<Object, String> templateTextEscaper;
+
+ /**
+ * Constructor.
+ *
+ * @param engine engine used to evaluate the template
+ * @param templateEncoding encoding used by the template
+ * @param escaper escape type. One of {@link #TEXT_ESCAPE}.
+ */
+ protected Template(@Nonnull final TemplateEngine engine, @Nonnull final Charset encoding, @Nonnull final String escaper) {
+ templateEngine = Constraint.isNotNull(engine, "TemplateEngine can not be null");
+ templateEncoding =
+ Constraint.isNotNull(encoding,
+ "Template encoding name can not be null or empty");
+ templateEscapeType =
+ Constraint.isNotNull(StringSupport.trimOrNull(escaper),
+ "Template escaping type can not be null or empty");
+ }
+
+ /**
+ * A convenience method that assumes the given template is US ASCII encoded.
+ *
+ * @param engine engine that will be used to evaluate the template
+ * @param templateText the literal Velocity template, <strong>NOT</strong> a template name see
+ *
+ * @return an instance of this class that can be used to evaluate the given template using the given engine
+ */
+ @Nonnull public static Template fromTemplate(@Nonnull final TemplateEngine engine,
+ @Nonnull @NotEmpty final String templateText) {
+ final String trimmedTemplate =
+ Constraint.isNotNull(StringSupport.trimOrNull(templateText), "Template can not be null or empty");
+ return engine.createTemplate(trimmedTemplate, true, Charsets.US_ASCII, TEXT_ESCAPE);
+ }
+
+ /**
+ * Constructs a {@link Template} from a given template.
+ *
+ * @param engine engine that will be used to evaluate the template
+ * @param templateText the literal Velocity template, <strong>NOT</strong> a template name
+ * @param encoding the encoding used by the template
+ *
+ * @return an instance of this class that can be used to evaluate the given template using the given engine
+ */
+ @Nonnull public static Template fromTemplate(@Nonnull final TemplateEngine engine,
+ @Nonnull @NotEmpty final String templateText,
+ @Nonnull final Charset encoding) {
+ final String trimmedTemplate =
+ Constraint.isNotNull(StringSupport.trimOrNull(templateText), "Template can not be null or empty");
+ Constraint.isNotNull(encoding, "Template encoding character set can not be null");
+
+ return engine.createTemplate(trimmedTemplate, true, encoding, TEXT_ESCAPE);
+ }
+
+ /**
+ * A convenience method that assumes the named template is US ASCII encoded.
+ *
+ * @param engine engine that will be used to evaluate the template
+ * @param templateName the name, as known to the given engine, of a velocity template
+ *
+ * @return an instance of this class that can be used to evaluate the named template using the given engine
+ */
+ @Nonnull public static Template fromTemplateName(@Nonnull final TemplateEngine engine,
+ @Nonnull @NotEmpty final String templateName) {
+ final String trimmedName =
+ Constraint.isNotNull(StringSupport.trimOrNull(templateName), "Template can not be null or empty");
+ return engine.createTemplate(trimmedName, true, Charsets.US_ASCII, TEXT_ESCAPE);
+ }
+
+ /**
+ * Constructs a {@link Template} that evaluates a named velocity template with a using the given velocity engine.
+ *
+ * @param engine the engine used to evaluate the template
+ * @param name the name of the template
+ * @param encoding the template encoding
+ *
+ * @return an instance of this class that can be used to evaluate the named template using the given engine
+ */
+ @Nonnull public static Template fromTemplateName(@Nonnull final TemplateEngine engine,
+ @Nonnull @NotEmpty final String name, @Nonnull final Charset encoding) {
+ final String trimmedName =
+ Constraint.isNotNull(StringSupport.trimOrNull(name), "Velocity template name can not be null or empty");
+ Constraint.isNotNull(encoding, "Template encoding character set can not be null");
+
+ return engine.createTemplate(trimmedName, true, Charsets.US_ASCII, TEXT_ESCAPE);
+ }
+
+ /**
+ * Evaluates the template using the given context and returns the result as a string.
+ *
+ * @param templateContext current template context
+ *
+ * @return the generated output of the template
+ */
+ @Nonnull public String merge(final TemplateContext templateContext) {
+ final StringWriter output = new StringWriter();
+ merge(templateContext, output);
+ final String result = output.toString();
+ assert result != null;
+ return result;
+ }
+
+ /**
+ * Evaluates the template using the given context and sends the result to a Writer.
+ *
+ * @param templateContext current template context
+ * @param output writer that will receive the template output
+ */
+ abstract public void merge(final TemplateContext templateContext, final Writer output);
+
+ protected Charset getTemplateEncoding() {
+ return templateEncoding;
+ }
+
+ /** Get the text escaper.
+ * @return
+ */
+ @Nullable
+ protected Function<Object, String> getTemplateTextEscaper() {
+ return templateTextEscaper;
+ }
+
+ public void setTemplateTextEscaper(@Nonnull Function<Object, String> textEscaper) {
+ if (!TEXT_ESCAPE.equals(templateEscapeType)) {
+ throw new ConstraintViolationException("Can only set aN Escaper on TEXT templates");
+ }
+ templateTextEscaper = textEscaper;
+ }
+
+
+}
diff --git a/shib-support/src/main/java/net/shibboleth/shared/template/TemplateContext.java b/shib-support/src/main/java/net/shibboleth/shared/template/TemplateContext.java
new file mode 100644
index 00000000..fb2488ba
--- /dev/null
+++ b/shib-support/src/main/java/net/shibboleth/shared/template/TemplateContext.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** The methods that a specific template engine needs to implement to be plugged into a {@link Template}. */
+package net.shibboleth.shared.template;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.shared.logic.Constraint;
+
+/** A per template engine abstraction of an templating context. */
+
+public class TemplateContext {
+
+ @Nonnull final Map<String, Object> context;
+
+ public TemplateContext() {
+ context = new HashMap<>();
+ }
+
+ public TemplateContext(@Nonnull Map<String, Object> input) {
+ context = Constraint.isNotNull(input, "Context Map must be non-null");
+ }
+
+ public Map<String, Object> getContext() {
+ return context;
+ }
+
+ /**
+ * Adds a name/value pair to the context.
+ *
+ * @param key The name to key the provided value with.
+ * @param value The corresponding value.
+ * @return Object that was replaced in the the Context if
+ * applicable or null if not.
+ */
+ public void put(String key, Object value)
+ {
+ context.put(key, value);
+ }
+}
diff --git a/shib-support/src/main/java/net/shibboleth/shared/template/TemplateEngine.java b/shib-support/src/main/java/net/shibboleth/shared/template/TemplateEngine.java
new file mode 100644
index 00000000..e0a56f75
--- /dev/null
+++ b/shib-support/src/main/java/net/shibboleth/shared/template/TemplateEngine.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** The methods that a specific template engine needs to implement to be plugged into a {@link Template}. */
+package net.shibboleth.shared.template;
+
+import java.nio.charset.Charset;
+
+/** The interface that a specific template engine has to implement. */
+public interface TemplateEngine {
+
+
+ /** Create a specific context for
+ * @return
+ */
+ //TemplateContext createContext();
+
+ /** Create a version of {@link Template} according to the parameterization.
+ * @param template the NAME or TEXT for a template
+ * @param isText if true then template is the template itself, if false then it is a template "name"
+ * @param encoding the encode to use
+ * @param textEscape The escaping to use
+ * @return
+ */
+ Template createTemplate(String template, boolean isText, Charset encoding, String textEscape);
+}
diff --git a/shib-support/src/main/java/net/shibboleth/shared/template/TemplateEngineTemplate.java b/shib-support/src/main/java/net/shibboleth/shared/template/TemplateEngineTemplate.java
new file mode 100644
index 00000000..2db3c543
--- /dev/null
+++ b/shib-support/src/main/java/net/shibboleth/shared/template/TemplateEngineTemplate.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** The methods that a specific template engine needs to implement to be plugged into a {@link Template}. */
+package net.shibboleth.shared.template;
+
+/** A per template engine abstraction of an template. */
+
+public interface TemplateEngineTemplate {
+
+
+}
diff --git a/shib-support/src/main/java/net/shibboleth/shared/template/package-info.java b/shib-support/src/main/java/net/shibboleth/shared/template/package-info.java
new file mode 100644
index 00000000..7ea6aac7
--- /dev/null
+++ b/shib-support/src/main/java/net/shibboleth/shared/template/package-info.java
@@ -0,0 +1,16 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** Classes for working with Templates. */
+package net.shibboleth.shared.template;
\ No newline at end of file
diff --git a/shib-velocity/src/main/java/net/shibboleth/shared/velocity/VelocityTemplateEngine.java b/shib-velocity/src/main/java/net/shibboleth/shared/velocity/VelocityTemplateEngine.java
new file mode 100644
index 00000000..e09a69c7
--- /dev/null
+++ b/shib-velocity/src/main/java/net/shibboleth/shared/velocity/VelocityTemplateEngine.java
@@ -0,0 +1,149 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.shared.velocity;
+
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.app.event.EventCartridge;
+import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+import org.apache.velocity.runtime.resource.util.StringResourceRepository;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.template.Template;
+import net.shibboleth.shared.template.TemplateContext;
+import net.shibboleth.shared.template.TemplateEngine;
+
+/**
+ *
+ */
+public final class VelocityTemplateEngine implements TemplateEngine {
+
+ /** The {@link VelocityEngine} used when evaluating the template. */
+ @Nonnull protected final VelocityEngine engine;
+
+ public VelocityTemplateEngine(@Nonnull final VelocityEngine velocityEngine) {
+ engine = Constraint.isNotNull(velocityEngine, "Velocity Engine must be nmon null");
+ }
+
+ @Override
+ public Template createTemplate(String template, boolean isText, Charset encoding, String textEscape) {
+ if (!isText) {
+ return new VelocityTemplate(encoding, textEscape, template);
+ }
+ final String trimmedTemplate =
+ Constraint.isNotNull(StringSupport.trimOrNull(template), "Velocity template can not be null or empty");
+ Constraint.isNotNull(encoding, "Template encoding character set can not be null");
+
+ final StringResourceRepository templateRepo = StringResourceLoader.getRepository();
+
+ String templateName;
+ do {
+ // keep generating a random name until we find one not already in use
+ // in theory it should be the first one, but just in case...
+ templateName = UUID.randomUUID().toString();
+ } while (templateRepo.getStringResource(templateName) != null);
+
+ templateRepo.putStringResource(templateName, trimmedTemplate, encoding.name());
+
+ if (!engine.resourceExists(templateName)) {
+ throw new VelocityException(
+ "Velocity engine is not configured to load templates from the default StringResourceRepository");
+ }
+
+ try {
+ engine.getTemplate(templateName);
+ } catch (final VelocityException e) {
+ throw new VelocityException("The following template is not valid:\n" + trimmedTemplate, e);
+ }
+
+ assert templateName != null;
+ return new VelocityTemplate(encoding, textEscape, templateName);
+ }
+
+ private class VelocityTemplate extends Template {
+
+ /** The name of the template to be evaluated. */
+ @Nonnull @NotEmpty private final String templateName;
+
+ private VelocityTemplate(final Charset encoding, String escaper, @Nonnull @NotEmpty final String velocityTemplateName) {
+ super(VelocityTemplateEngine.this, encoding, escaper);
+ templateName = Constraint.isNotNull(StringSupport.trimOrNull(velocityTemplateName),
+ "Velocity template name can not be null or empty");
+ }
+
+ @Override
+ public void merge(TemplateContext templateContext, Writer output) {
+ final VelocityContext velocityContext = new VelocityContext();
+ for (Entry<String, Object> entry:templateContext.getContext().entrySet()) {
+ velocityContext.put(entry.getKey() , entry.getValue());
+ }
+
+ if (getTemplateTextEscaper() != null) {
+ final EventCartridge cartridge = new EventCartridge();
+ cartridge.addEventHandler(new EscapingReferenceInsertionEventHandler());
+ cartridge.attachToContext(velocityContext);
+ }
+
+ try {
+ VelocityTemplateEngine.this.engine.mergeTemplate(templateName, getTemplateEncoding().name(), velocityContext, output);
+ } catch (final ResourceNotFoundException e) {
+ throw new VelocityException("Velocity template " + templateName
+ + " has been removed since this object was constructed");
+ } catch (final Exception e) {
+ throw new VelocityException("Velocity template " + templateName + " threw an exception", e);
+ }
+ }
+
+ private class EscapingReferenceInsertionEventHandler implements ReferenceInsertionEventHandler {
+
+ @Override public Object referenceInsert(final Context context, final String reference, final Object value) {
+ if (value == null) {
+ return null;
+ } else if (value instanceof Object[]) {
+ final List<Object> encodedValues = new ArrayList<>();
+ for (final Object o : (Object[]) value) {
+ encodedValues.add(getTemplateTextEscaper().apply(o));
+ }
+ return encodedValues.toArray();
+ } else if (value instanceof Collection) {
+ final List<Object> encodedValues = new ArrayList<>();
+ for (final Object o : (Collection<?>) value) {
+ encodedValues.add(getTemplateTextEscaper().apply(o));
+ }
+ return encodedValues;
+ } else {
+ return getTemplateTextEscaper().apply(value);
+ }
+ }
+
+ }
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list