How to generate static files from a Spring Thymeleaf project? - spring

I have a website that was built using Spring Thymeleaf, and I'd like to generate the files, such as an index.html, css, and js files, in a flat directory so that I can serve the compiled/transpiled files from an AWS S3 bucket. The project is a Gradle project. How do I do this? Thank you in advance.

You can run Thymeleaf as a Java program (rather than on a server) and generate HTML files like this:
public class Application {
public static void main(String... args) {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(new AnnotationConfigApplicationContext());
resolver.setPrefix("classpath:/html/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("UTF-8");
resolver.setTemplateMode(TemplateMode.HTML);
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(resolver);
Context context = new Context();
String html = engine.process("index", context);
System.out.println(html);
}
}
Simply replace the System.out.println(html); with saving the html variable to a file. Then you just have to read your directory for all the files you want to interpret and run the code for everything.
This will not work for forms -- but I'm assuming you don't need any actually dynamic content if you are trying to compile your Thymeleaf to static html.
You will also have to include the right dependencies. I use these:
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>

Related

how to hide this button during convert of html page to pdf using iText in Spring Boot and Thymleaf

I am facing problem during pdf generation when I am converting html page into pdf that time button is also showing, but requirement is button should not be show but button should be on same page and after click PDF should be generate. I am using iText for Pdf generation. I am using Thymeleaf and Spring Boot.
I have used this code for pdf generation.
#RequestMapping("download-pdf/{refno}")
public ResponseEntity<?> getPDF(HttpServletRequest request, HttpServletResponse response,#PathVariable("refno") Long refno) throws IOException, IllegalAccessException, InvocationTargetException {
complaintDto = complaintRepo.findById(refno).orElse(null);
complaintPdfBean = pdfService.getComplaintInfo(complaintDto);
WebContext context = new WebContext(request, response, servletContext);
context.setVariable("complaintPdfBean", complaintPdfBean);
String grievanceHtml = templateEngine.process("complant-privew", context);
ByteArrayOutputStream target = new ByteArrayOutputStream();
ConverterProperties converterProperties = new ConverterProperties();
converterProperties.setBaseUri("http://localhost:8080");
HtmlConverter.convertToPdf(grievanceHtml, target, converterProperties);
byte[] bytes = target.toByteArray();
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=grievance.pdf")
.contentType(MediaType.APPLICATION_PDF).body(bytes);
}
I am using this maven dependency in pom.xml file:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.1.0</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>2.0.0</version>
</dependency>
<!-- pdf dependency -->
Wrap your button with a <th:block> and an th:unless statement:
<th:block th:unless="${isPdfExport}">
...
</th:block>
Set the isPdfExport context variable to true just before the PDF generation.

Object distortion while passing from REST service to Spring app

I've got strange problem and I hope you will to help me to solve it.
I try to pass list of objects, where each object contains LocalDate parameter (JodaTime library) from test service to my controller.
This is method from my service. It returns list of objects. Look at the dates printed out in the loop.
#RequestMapping("/getListaRecept")
#ResponseBody
public ListaRecept sendAnswer(){
ListaRecept listaReceptFiltered = prescriptionCreator.createListaRecept();
for(Recepta r : listaReceptFiltered.getListaRecept()){
System.out.println(r.toString());
}
return listaReceptFiltered;
}
Dates are correct
Recepta{id=3, nazwa='nurofen', status=NOT_REALIZED, date=2017-07-27}
Recepta{id=1, nazwa='ibuprom', status=ANNULED, date=2014-12-25}
Recepta{id=2, nazwa='apap', status=REALIZED, date=2016-08-18}
And now I'm invoking this method from my SpringBoot app using restTemplate. And then received list is printed out
private final RestTemplate restTemplate;
public SgrService2(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
this.restTemplate.getMessageConverters()
.add(0, new StringHttpMessageConverter(Charset.forName("UTF-16")));
}
public ListaRecept getList() {
for(Recepta r : this.restTemplate.getForObject("http://localhost:8090/getListaRecept",
ListaRecept.class).getListaRecept()){
System.out.println(r.toString());
}
return this.restTemplate.getForObject("http://localhost:8090/getListaRecept",
ListaRecept.class);
}
As you can see all dates were replaced with current date :/
Recepta{id=3, nazwa='nurofen', status=NOT_REALIZED, date=2017-09-30}
Recepta{id=1, nazwa='ibuprom', status=ANNULED, date=2017-09-30}
Recepta{id=2, nazwa='apap', status=REALIZED, date=2017-09-30}
I have no idea what is going on...
Here you have pom dependencies
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.1</version>
</dependency>
Thank you in advance for your help
It seems to me that you are using the wrong jackson module, instead of jsr310 (which I guess is for Java 8 date types), try using the artifact jackson-datatype-joda and register the module JodaModule.

spring boot elastic search -configure data source

I am tryinging to configure spring data boot sand ES project
in my pom.xml i have :
#Configuration
#EnableElasticsearchRepositories(basePackages = "com.yoyo.elastic.repository")
public class ElasticConfiguration {
#Bean
public NodeBuilder nodeBuilder() {
return new NodeBuilder();
}
#Bean
public ElasticsearchOperations elasticsearchTemplate() throws IOException {
File tmpDir = File.createTempFile("elastic", Long.toString(System.nanoTime()));
System.out.println("Temp directory: " + tmpDir.getAbsolutePath());
final Client client = nodeBuilder().local(true).node().client();
return new ElasticsearchTemplate(client);
}
}
in my pom xml I have this dep :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
which should supplay the driver but i keep on getting :
Description:
Cannot determine embedded database driver class for database type NONE
Action:
If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).
I had the same issue when trying to run some exercises with Spring Boot and ElasticSearch.
Right now I figured out that if you have the
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Alongside spring-boot-starter-data-elasticsearch and don't add additional config classes (where you would configure the DataSource) spring boot will complain.
Other solution would be to actually add a datasource property to application.properties and configure standalone database (like H2)

Library for distributed spring config (outside springboot)

I am looking for solution for distributed spring configuration. I am thinking of storing it in zookeeper. https://github.com/spring-cloud/spring-cloud-zookeeper does have that functionality but apparently it requires to use spring-boot.
Is there any similar library that I can use outside spring-boot
Consul by HashiCorp
Consul is a popular option because it is:
Open Source
Includes Service Discovery & Configuration
Support Multi-Datacenter out of the box
Etc.
It doesn't require you to use Spring Boot, it just provides the auto-configurations in case you do decide to go with Spring Boot. In other words, if you're not using Spring Boot, none of the configurations will apply automatically, you'll have to provide the configuration yourself.
Zookeeper is a good option, go for it.
EDIT:
To use Zookeeper without Spring Boot, you'd need to register the appropriate beans either manually or by importing the auto-configuration classes that Spring Boot would import for you implicitly. This rule of thumb generally applies to all Spring Boot-enabled modules.
In your case, you'd most likely need to import just the ZookeeperConfigBootstrapConfiguration and ZookeeperConfigAutoConfiguration. The classes are to be found within spring-cloud-zookeeper-config module so no Spring Boot dependencies needed.
Alternatively, you should look at those classes and their #Imports and declare the beans manually.
I found a solution for using spring-cloud-zookeeper without Spring Boot, based on the idea provided here https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html
First, create a CloudEnvironement class that will create a PropertySource from Zookeeper :
CloudEnvironement.java
public class CloudEnvironment extends StandardServletEnvironment {
#Override
protected void customizePropertySources(MutablePropertySources propertySources) {
super.customizePropertySources(propertySources);
try {
propertySources.addLast(initConfigServicePropertySourceLocator(this));
}
catch (Exception ex) {
logger.warn("failed to initialize cloud config environment", ex);
}
}
private PropertySource<?> initConfigServicePropertySourceLocator(Environment environment) {
ZookeeperConfigProperties configProp = new ZookeeperConfigProperties();
ZookeeperProperties props = new ZookeeperProperties();
props.setConnectString("myzookeeper:2181");
CuratorFramework fwk = curatorFramework(exponentialBackoffRetry(props), props);
ZookeeperPropertySourceLocator propertySourceLocator = new ZookeeperPropertySourceLocator(fwk, configProp);
PropertySource<?> source= propertySourceLocator.locate(environment);
return source ;
}
private CuratorFramework curatorFramework(RetryPolicy retryPolicy, ZookeeperProperties properties) {
CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
builder.connectString(properties.getConnectString());
CuratorFramework curator = builder.retryPolicy(retryPolicy).build();
curator.start();
try {
curator.blockUntilConnected(properties.getBlockUntilConnectedWait(), properties.getBlockUntilConnectedUnit());
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
return curator;
}
private RetryPolicy exponentialBackoffRetry(ZookeeperProperties properties) {
return new ExponentialBackoffRetry(properties.getBaseSleepTimeMs(),
properties.getMaxRetries(),
properties.getMaxSleepMs());
}
}
Then create a custom XmlWebApplicationContext class : it will enable to load the PropertySource from Zookeeper when your webapplication start and replace the bootsrap magic of Spring Boot:
MyConfigurableWebApplicationContext.java
public class MyConfigurableWebApplicationContext extends XmlWebApplicationContext {
#Override
protected ConfigurableEnvironment createEnvironment() {
return new CloudEnvironment();
}
}
Last, in your web.xml file add the following context-param for using your MyConfigurableWebApplicationContext class and bootstraping your CloudEnvironement.
<context-param>
<param-name>contextClass</param-name>
<param-value>com.kiabi.config.MyConfigurableWebApplicationContext</param-value>
</context-param>
If you use a standard property file configurer, it should still be loaded so you can have properties in both a local file and Zookeeper.
For all this to work you need to have spring-cloud-starter-zookeeper-config and curator-framework jar in your classpath with their dependancy, if you use maven you can add the following to your pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper-dependencies</artifactId>
<version>1.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-config</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
</dependencies>

Testing with spring-test-mvc jsonpath returns null

I am using Spring's "spring-test-mvc" library to test web controllers. I have a very simple controller that returns a JSON array. Then in my test I have:
#Test
public void shouldGetAllUsersAsJson() throws Exception {
mockMvc.perform(get("/v1/users").accept(MediaType.APPLICATION_JSON))
.andExpect(content().mimeType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("fName").exists());
}
The above test returns:
java.lang.AssertionError: No value for JSON path: fName
To quickly check what I actually get I ran the below test:
#Test
public void shouldPrintResults() throws Exception {
mockMvc.perform(get("/v1/users").accept(MediaType.APPLICATION_JSON))
.andDo(print());
}
And it returns the correct JSON array in the body of MockHttpServletResponse
I'm not sure why jsonPath is not able to see fName in the JSON array.
If you add the json path dependency to maven, or add the jar to your lib, then it will work. I think that Spring is not including the jsonPath dependency in the latest Spring 3.2.0 RC1 release. I'm guessing that this is the same for Spring-Test-MVC standalone project as well.
Here is the dependency for Maven:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.8.1</version>
<scope>test</scope>
</dependency>
You might also need the hamcrest library to use the jsonPath("$.test").value("test")
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
What does your json response body look like? You can see it by doing an .andDo(print())
You might want to try jsonPath("$.fName").
This is assuming that your json response is:
{"fName":"first name"}
If your response is an array then you need jsonPath("$[0].fName") for a response like:
[{"fName":"first name"},{"fName":"first name #2"}]
You can see more examples at: http://goessner.net/articles/JsonPath/

Resources