我正在开发一个 Spring Boot 项目,我使用 Freemarker 进行模板化。我在 Freemarker 的配置中配置了多个 TemplateLoader,以从两个不同的 CDN URL 加载模板:baselineUrl 和dynamicUrl。这个想法是,如果在baselineUrl中找不到模板,它应该回退到dynamicUrl。
这是我的配置:
@Slf4j
@Configuration
@ConfigurationProperties("email")
@Data
public class FtlConfiguration {
// https://dynamic/bhnpay-static/notification-template/ftl
private String baselineUrl;
// https://baseline/bhnpay-static/notification-template/ftl
private String dynamicUrl;
private long retentionTimeMillis;
@Bean("emailConfiguration")
public freemarker.template.Configuration createFtlConfigBean() throws IOException {
freemarker.template.Configuration cfg = new freemarker.template.Configuration(
freemarker.template.Configuration.VERSION_2_3_31);
cfg.setDefaultEncoding(NotificationConstants.UTF_8);
cfg.setLogTemplateExceptions(false);
cfg.setWrapUncheckedExceptions(true);
cfg.setFallbackOnNullLoopVariable(false);
cfg.setLocalizedLookup(false);
List<TemplateLoader> loaders = new ArrayList<>();
// CloudTemplateLoader is just a wrapper on UrlTemplateLoader
loaders.add(new CloudTemplateLoader(new URL(baselineUrl)));
loaders.add(new CloudTemplateLoader(new URL(dynamicUrl)));
cfg.setTemplateLoader(
new MultiTemplateLoader(loaders.toArray(new TemplateLoader[0])));
cfg.setCacheStorage(new MruCacheStorage(5000, Integer.MAX_VALUE));
cfg.setTemplateUpdateDelayMilliseconds(retentionTimeMillis);
return cfg;
}
}
以下是我尝试加载模板的方式:
@Component
@Slf4j
public class FtlTemplateHandler {
private final Configuration configuration;
@Autowired
public FtlTemplateHandler(@Qualifier("emailConfiguration") Configuration configuration) {
this.configuration = configuration;
}
public String getTemplateAsHtml(String emailTemplateUrl) {
try {
Template template = configuration.getTemplate(emailTemplateUrl);
// Process the template
} catch (IOException e) {
try {
log.info("removing template from cache {}", emailTemplateUrl);
configuration.removeTemplateFromCache(emailTemplateUrl);
Thread.sleep(10000);
} catch (IOException | InterruptedException ex) {
// process exception
}
}
}
}
我的云模板加载器:
@Slf4j
public class CloudTemplateLoader extends URLTemplateLoader {
private URL root;
public CloudTemplateLoader(URL root) {
super();
this.root = root;
}
public URL getRoot() {
return root;
}
public void setRoot(URL root) {
this.root = root;
}
@Override
protected URL getURL(String template) {
try {
return new URL(root, template);
} catch (MalformedURLException e) {
log.error("Error while loading templates from cloud.");
}
return null;
}
}
出现以下错误:
Caused by: java.io.IOException: Server returned HTTP response code: 403 for URL: https://baseline/bhnpay-static/notification-template/ftl/en-US/b9b09aa4-fd39-4812-924e-1fe7f6d4ed51.ftl
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:2000)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1589)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getHeaderField(HttpURLConnection.java:3256)
at java.base/java.net.HttpURLConnection.getHeaderFieldDate(HttpURLConnection.java:601)
at java.base/java.net.URLConnection.getLastModified(URLConnection.java:567)
at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getLastModified(HttpsURLConnectionImpl.java:392)
at freemarker.cache.URLTemplateSource.lastModified(URLTemplateSource.java:94)
at freemarker.cache.URLTemplateLoader.getLastModified(URLTemplateLoader.java:50)
at freemarker.cache.MultiTemplateLoader$MultiSource.getLastModified(MultiTemplateLoader.java:142)
at freemarker.cache.MultiTemplateLoader.getLastModified(MultiTemplateLoader.java:99)
at freemarker.cache.TemplateCache.getTemplateInternal(TemplateCache.java:439)
但是模板存在于动态 url 中,这意味着在查看基线 cdn 后,它不会检查动态 cdn。
我已验证 URL 正确且可访问。 我尝试清除缓存以确保这不是缓存问题,但回退仍然不起作用。
在返回之前验证与 url 的连接实际上对我有用。
@Override
protected URL getURL(String template) {
try {
URL url = new URL(root, template);
if (isTemplateAvailable(url)) {
return url;
} else {
log.warn("Template not found or inaccessible at URL: {}", url);
}
} catch (MalformedURLException e) {
log.error("Malformed URL when loading template {}: ", template, e);
} catch (IOException e) {
log.error("IOException when checking template availability: ", e);
}
return null;
}
private boolean isTemplateAvailable(URL url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("HEAD");
connection.connect();
int responseCode = connection.getResponseCode();
connection.disconnect();
return responseCode == HttpURLConnection.HTTP_OK;
}