1
0
mirror of https://github.com/google/nomulus synced 2026-05-27 18:20:32 +00:00
Files
nomulus/common/src/main/java/google/registry/util/TemplateRenderer.java
Ben McIlwain 53b92d602e Migrate EPP/Email from Soy to JAXB/FreeMarker (#3038)
- Replace deprecated Soy templates for EPP XML with JAXB models and a refined Fluent DSL.
- Migrate Spec11 and administrative emails to FreeMarker with HTML auto-escaping.
- Remove Soy compiler, Gradle tasks, and library dependencies.
- Address PR feedback regarding shadowing, version locking, and security warnings.
- Enhance tests with comprehensive XML equality assertions using Java 15 text blocks.
- Improve Javadocs and maintain strict temporal consistency using java.time.

FreeMarker replaces Soy for email templating, providing native HTML auto-escaping and allowing the removal of the complex 'soyToJava' compilation step from the build process. This significantly simplifies the build system and reduces maintenance overhead. For EPP XML, migrating to JAXB allows tool-generated commands to use the same model classes as the server-side EPP flows. This ensures that tool-generated XML is always schema-compliant and eliminates the risk of divergence between tool templates and actual server-side implementation. This unified approach provides compile-time type safety and improves developer ergonomics via a refined fluent DSL.

The base ImmutableObject class now provides a public clone() override that correctly resets the cached hashCode to null. This centralizes the custom cloning logic previously handled by a static helper and ensures that all subclasses—including the newly added JAXB models—satisfy CodeQL security requirements without needing redundant per-class overrides. The legacy static clone(T) helper has been updated to delegate to this instance method to maintain compatibility and architectural consistency.
2026-05-22 19:33:47 +00:00

68 lines
2.7 KiB
Java

// Copyright 2026 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.util;
import com.google.common.collect.ImmutableMap;
import freemarker.core.HTMLOutputFormat;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import jakarta.inject.Inject;
import java.io.StringWriter;
/**
* A utility class for rendering FreeMarker templates.
*
* <p>This renderer is configured to use HTML as the default output format, which enables automatic
* escaping of all interpolated variables. It also uses the "computer" number format to ensure
* consistent formatting of numeric values across different locales.
*/
public class TemplateRenderer {
private final Configuration configuration;
@Inject
public TemplateRenderer() {
this.configuration = new Configuration(Configuration.VERSION_2_3_32);
this.configuration.setClassLoaderForTemplateLoading(getClass().getClassLoader(), "");
this.configuration.setDefaultEncoding("UTF-8");
this.configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
this.configuration.setLogTemplateExceptions(false);
this.configuration.setWrapUncheckedExceptions(true);
this.configuration.setFallbackOnNullLoopVariable(false);
this.configuration.setOutputFormat(HTMLOutputFormat.INSTANCE);
this.configuration.setNumberFormat("computer");
}
/**
* Renders the specified template with the given data model.
*
* @param templatePath the path to the template file relative to the classpath root
* @param dataModel an immutable map containing the data to be used in the template
* @return the rendered template as a string
* @throws RuntimeException if the template cannot be found, parsed, or processed
*/
public String render(String templatePath, ImmutableMap<String, Object> dataModel) {
try {
Template template = configuration.getTemplate(templatePath);
StringWriter writer = new StringWriter();
template.process(dataModel, writer);
return writer.toString();
} catch (Exception e) {
throw new RuntimeException(String.format("Error rendering template %s", templatePath), e);
}
}
}