Skip to content

Commit

Permalink
refactored spring starter (#251)
Browse files Browse the repository at this point in the history
* Use a PrintWriterOutput with the writer from the response and don't write directly to the outputStream.

This allows for capturing the response with a RepsponseWriter like here in this example: https://github.com/tschuehly/spring-view-component/blob/41c57fbd2acbe7439982b60291765e3586a7fbc8/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionFilter.kt

* usePrecompiledTemplates property instead of productionProfileName as this makes it clear that you need to precompile the templates with gradle/maven
AutoConfiguration: switched to constructor inject
fixed missing templateSuffix configuration in ReactiveJteViewResolver

added spring-configuration-metadata for IDE IntelliSense in application.properties
updated readme

* Align JteView

* remove templateEngine.setBinaryStaticContent(true) and usePrecompiledTemplates is boolean

* remove boxing of boolean

* add developmentMode and adjust README
  • Loading branch information
tschuehly authored Aug 29, 2023
1 parent 62159c1 commit f432fcd
Show file tree
Hide file tree
Showing 14 changed files with 171 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,15 @@
@EnableConfigurationProperties(JteProperties.class)
public class JteAutoConfiguration {

@Autowired
private Environment environment;
private final JteProperties jteProperties;

@Autowired
private JteProperties jteProperties;
public JteAutoConfiguration(JteProperties jteProperties) {
this.jteProperties = jteProperties;
}

@Bean
@ConditionalOnMissingBean(JteViewResolver.class)
public JteViewResolver jteViewResolver(TemplateEngine templateEngine) {

return new JteViewResolver(templateEngine, jteProperties.getTemplateSuffix());
}

Expand All @@ -39,8 +38,8 @@ public JteViewResolver jteViewResolver(TemplateEngine templateEngine) {
@ConditionalOnMissingBean(TemplateEngine.class)
public TemplateEngine jteTemplateEngine() {

if (jteProperties.isProductionEnabled(environment)) {
// Templates will be compiled by the maven build task
if (jteProperties.usePreCompiledTemplates()) {
// Templates will need to be compiled by the maven/gradle build task
return TemplateEngine.createPrecompiled(ContentType.Html);
} else {
// Here, a JTE file watcher will recompile the JTE templates upon file save (the web browser will auto-refresh)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
package gg.jte.springframework.boot.autoconfigure;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.lang.NonNull;

import java.util.Arrays;

@ConfigurationProperties(prefix = "gg.jte")
public class JteProperties {

private String productionProfileName = "prod";
private Boolean usePrecompiledTemplates = false;
private String templateLocation = "src/main/jte";
private String templateSuffix = ".jte";

Expand All @@ -21,14 +16,6 @@ public void setTemplateSuffix(final String templateSuffix) {
this.templateSuffix = templateSuffix;
}

public String getProductionProfileName() {
return productionProfileName;
}

public void setProductionProfileName(String productionProfileName) {
this.productionProfileName = productionProfileName;
}

public String getTemplateLocation() {
return templateLocation;
}
Expand All @@ -37,7 +24,11 @@ public void setTemplateLocation(String templateLocation) {
this.templateLocation = templateLocation;
}

public boolean isProductionEnabled(@NonNull Environment environment) {
return Arrays.stream(environment.getActiveProfiles()).anyMatch(it -> it.equals(this.getProductionProfileName()));
public Boolean usePreCompiledTemplates() {
return usePrecompiledTemplates;
}

public void setUsePrecompiledTemplates(Boolean usePrecompiledTemplates) {
this.usePrecompiledTemplates = usePrecompiledTemplates;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package gg.jte.springframework.boot.autoconfigure;

import gg.jte.TemplateEngine;
import gg.jte.output.Utf8ByteOutput;
import gg.jte.output.PrintWriterOutput;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.view.AbstractTemplateView;

Expand All @@ -24,16 +24,10 @@ public boolean checkResource(Locale locale) {

@Override
protected void renderMergedTemplateModel(Map<String, Object> model, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception {

String url = this.getUrl();

Utf8ByteOutput output = new Utf8ByteOutput();
templateEngine.render(url, model, output);

response.setContentType(MediaType.TEXT_HTML_VALUE);
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentLength(output.getContentLength());

output.writeTo(response.getOutputStream());
PrintWriterOutput output = new PrintWriterOutput(response.getWriter());
templateEngine.render(url, model, output);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,25 @@
@ConditionalOnClass({TemplateEngine.class, UrlBasedViewResolver.class})
@EnableConfigurationProperties(JteProperties.class)
public class ReactiveJteAutoConfiguration {
private final JteProperties jteProperties;

@Autowired
private Environment environment;

@Autowired
private JteProperties jteProperties;
public ReactiveJteAutoConfiguration(JteProperties jteProperties) {
this.jteProperties = jteProperties;
}

@Bean
@ConditionalOnMissingBean(ReactiveJteViewResolver.class)
public ReactiveJteViewResolver reactiveJteViewResolver(TemplateEngine templateEngine) {

return new ReactiveJteViewResolver(templateEngine);
return new ReactiveJteViewResolver(templateEngine,jteProperties.getTemplateSuffix());
}

@Bean
@ConditionalOnMissingBean(TemplateEngine.class)
public TemplateEngine jteTemplateEngine() {

if (jteProperties.isProductionEnabled(environment)) {
// Templates will be compiled by the maven build task
if (jteProperties.usePreCompiledTemplates()) {
// Templates will need to be compiled by the maven/gradle build task
return TemplateEngine.createPrecompiled(ContentType.Html);
} else {
// Here, a JTE file watcher will recompile the JTE templates upon file save (the web browser will auto-refresh)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ public class ReactiveJteViewResolver extends UrlBasedViewResolver {

private final TemplateEngine templateEngine;

public ReactiveJteViewResolver(TemplateEngine templateEngine) {
public ReactiveJteViewResolver(TemplateEngine templateEngine, String templateSuffix) {
this.templateEngine = templateEngine;
this.setSuffix(".jte");
this.setSuffix(templateSuffix);
this.setViewClass(ReactiveJteView.class);
this.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"groups": [
{
"name": "gg.jte",
"type": "gg.jte.springframework.boot.autoconfigure.JteProperties",
"sourceType": "gg.jte.springframework.boot.autoconfigure.JteProperties"
}
],
"properties": [
{
"name": "gg.jte.usePrecompiledTemplates",
"type": "java.lang.Boolean",
"description": "You can precompile the templates for faster startup and rendering. To use this you need to include the Maven or Gradle Plugin: https://github.com/casid/jte/blob/main/DOCUMENTATION.md#precompiling-templates",
"sourceType": "gg.jte.springframework.boot.autoconfigure.JteProperties"
},
{
"name": "gg.jte.templateLocation",
"type": "java.lang.String",
"description": "You can change the location where jte expects the templates to compile",
"sourceType": "gg.jte.springframework.boot.autoconfigure.JteProperties"
},
{
"name": "gg.jte.templateSuffix",
"type": "java.lang.String",
"description": "You can configure the file suffix of jte templates the compiler resolves",
"sourceType": "gg.jte.springframework.boot.autoconfigure.JteProperties"
},
],
"hints": []
}
28 changes: 22 additions & 6 deletions jte-spring-boot-starter-3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,31 @@ You can use it with Spring WebMVC as well as with Spring WebFlux.

## Configuration

By default, the template files are expected in `src/main/jte`.
By default, the template files are expected in `src/main/jte`,
You can also set the templateSuffix of your jte templates

````properties
gg.jte.templateLocation=src/main/jte
gg.jte.templateSuffix=.jte
````

If any active profile is named prod the template engine will be configured
to use precompiled templates otherwise the jte file watcher will watch for changes in templates and recompile them.

Both options can be changed via
### Development

If you set development = true the jte file watcher will watch for changes in templates and recompile them.

! This only works with a JDK !
````properties
gg.jte.developmentMode=true
````
gg.jte.productionProfileName=k8s
gg.jte.templateLocation=src/main/templates


### Production

To use precompiled Templates in Production for use with a JRE environment you need to configure the Maven/Gradle Plugin to precompile your templates:
https://github.com/casid/jte/blob/main/DOCUMENTATION.md#precompiling-templates

````properties
gg.jte.usePrecompiledTemplates=true
````

Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
package gg.jte.springframework.boot.autoconfigure;

import gg.jte.*;
import gg.jte.resolve.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.*;
import org.springframework.context.annotation.*;
import org.springframework.core.env.*;
import org.springframework.web.servlet.view.*;

import java.nio.file.*;
import gg.jte.CodeResolver;
import gg.jte.ContentType;
import gg.jte.TemplateEngine;
import gg.jte.resolve.DirectoryCodeResolver;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.view.AbstractTemplateViewResolver;

import java.nio.file.FileSystems;
import java.nio.file.Paths;

@AutoConfiguration
@ConditionalOnClass({TemplateEngine.class, AbstractTemplateViewResolver.class})
@EnableConfigurationProperties(JteProperties.class)
public class JteAutoConfiguration {
private final JteProperties jteProperties;

@Autowired
private Environment environment;

@Autowired
private JteProperties jteProperties;
public JteAutoConfiguration(JteProperties jteProperties) {
this.jteProperties = jteProperties;
}

@Bean
@ConditionalOnMissingBean(JteViewResolver.class)
Expand All @@ -34,19 +35,20 @@ public JteViewResolver jteViewResolver(TemplateEngine templateEngine) {
@Bean
@ConditionalOnMissingBean(TemplateEngine.class)
public TemplateEngine jteTemplateEngine() {

if (jteProperties.isProductionEnabled(environment)) {
// Templates will be compiled by the maven build task
if(jteProperties.isDevelopmentMode() && jteProperties.usePreCompiledTemplates()){
throw new JteConfigurationException("You can't use development mode and precompiledTemplates together");
}
if (jteProperties.usePreCompiledTemplates()) {
// Templates will need to be compiled by the maven/gradle build task
return TemplateEngine.createPrecompiled(ContentType.Html);
} else {
}
if(jteProperties.isDevelopmentMode()) {
// Here, a JTE file watcher will recompile the JTE templates upon file save (the web browser will auto-refresh)
// If using IntelliJ, use Ctrl-F9 to trigger an auto-refresh when editing non-JTE files.
String[] split = jteProperties.getTemplateLocation().split("/");
CodeResolver codeResolver = new DirectoryCodeResolver(FileSystems.getDefault().getPath("", split));
TemplateEngine templateEngine = TemplateEngine.create(codeResolver, Paths.get("jte-classes"), ContentType.Html, getClass().getClassLoader());
templateEngine.setBinaryStaticContent(true);
return templateEngine;
return TemplateEngine.create(codeResolver, Paths.get("jte-classes"), ContentType.Html, getClass().getClassLoader());
}

throw new JteConfigurationException("You need to either set gg.jte.usePrecompiledTemplates or gg.jte.developmentMode to true ");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package gg.jte.springframework.boot.autoconfigure;

public class JteConfigurationException extends RuntimeException {
public JteConfigurationException(String message) {
super(message);
}

public JteConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package gg.jte.springframework.boot.autoconfigure;

import org.springframework.boot.context.properties.*;
import org.springframework.core.env.*;
import org.springframework.lang.*;

import java.util.*;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "gg.jte")
public class JteProperties {

private String productionProfileName = "prod";
private boolean usePrecompiledTemplates = false;
private boolean developmentMode = false;
private String templateLocation = "src/main/jte";
private String templateSuffix = ".jte";

Expand All @@ -21,14 +17,6 @@ public void setTemplateSuffix(final String templateSuffix) {
this.templateSuffix = templateSuffix;
}

public String getProductionProfileName() {
return productionProfileName;
}

public void setProductionProfileName(String productionProfileName) {
this.productionProfileName = productionProfileName;
}

public String getTemplateLocation() {
return templateLocation;
}
Expand All @@ -37,7 +25,19 @@ public void setTemplateLocation(String templateLocation) {
this.templateLocation = templateLocation;
}

public boolean isProductionEnabled(@NonNull Environment environment) {
return Arrays.stream(environment.getActiveProfiles()).anyMatch(it -> it.equals(this.getProductionProfileName()));
public boolean usePreCompiledTemplates() {
return usePrecompiledTemplates;
}

public void setUsePrecompiledTemplates(Boolean usePrecompiledTemplates) {
this.usePrecompiledTemplates = usePrecompiledTemplates;
}

public boolean isDevelopmentMode() {
return developmentMode;
}

public void setDevelopmentMode(boolean developmentMode) {
this.developmentMode = developmentMode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,10 @@ public boolean checkResource(Locale locale) {
@Override
protected void renderMergedTemplateModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
String url = this.getUrl();

Utf8ByteOutput output = new Utf8ByteOutput();
templateEngine.render(url, model, output);

response.setContentType(MediaType.TEXT_HTML_VALUE);
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentLength(output.getContentLength());

output.writeTo(response.getOutputStream());
PrintWriterOutput output = new PrintWriterOutput(response.getWriter());
templateEngine.render(url, model, output);
}

}
Loading

0 comments on commit f432fcd

Please sign in to comment.