* BAEL-5575

First commit of the example for the article:
https://drafts.baeldung.com/wp-admin/post.php?post=153774&action=edit

* BAEL-5575

Adjusted pom.xml

* BAEL-5575

Formatted source code

* BAEL-5575

Removed unused import

* BAEL-5575

Renamed UT to follow: https://docs.google.com/document/d/1iIeSPuYFG-jEtB8UbcxQnn2AI3OR2LWlnisNo2NUCL8/edit

* BAEL-5575

Modified pom.xml to define versions in  properties  as stated in:
https://docs.google.com/document/d/1iIeSPuYFG-jEtB8UbcxQnn2AI3OR2LWlnisNo2NUCL8/edit

* BAEL-5575

Changes checking the commands:
* mvn clean install
* mvn clean install -Dgib.enabled=false

* BAEL-5575

Set GB as the base language
This commit is contained in:
victorsempere 2023-05-20 10:05:43 +02:00 committed by GitHub
parent 50faedd2bc
commit 9fb2b4d919
32 changed files with 1453 additions and 0 deletions

View File

@ -0,0 +1,4 @@
--add-opens=java.base/java.util=ALL-UNNAMED
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED
--add-opens=java.base/java.text=ALL-UNNAMED
--add-opens=java.desktop/java.awt.font=ALL-UNNAMED

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung.spring-thymeleaf-attributes.module</groupId>
<artifactId>accessing-session-attributes</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>com.baeldung.spring-thymeleaf-attributes</groupId>
<artifactId>spring-thymeleaf-attributes-modules</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>${thymeleaf.spring6.version}</version>
</dependency>
<!-- test scoped -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.engine.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.engine.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<jvmArguments>
-Dfile.encoding="UTF-8" -Xdebug
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
</jvmArguments>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<start-class>com.baeldung.accesing_session_attributes.SpringWebApplicationInitializer</start-class>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<file.encoding>UTF-8</file.encoding>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
<junit.jupiter.engine.version>5.9.3</junit.jupiter.engine.version>
<mockito.version>5.3.1</mockito.version>
<thymeleaf.spring6.version>3.1.1.RELEASE</thymeleaf.spring6.version>
</properties>
</project>

View File

@ -0,0 +1,22 @@
package com.baeldung.accesing_session_attributes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringWebApplicationInitializer {
private Logger log = LoggerFactory.getLogger(getClass());
public SpringWebApplicationInitializer() {
super();
String encoding = System.getProperty("file.encoding");
log.info(encoding);
}
public static void main(String[] args) {
SpringApplication.run(SpringWebApplicationInitializer.class, args);
}
}

View File

@ -0,0 +1,11 @@
package com.baeldung.accesing_session_attributes.business;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import com.baeldung.accesing_session_attributes.business.entities.NameAgeEntity;
@Service
public interface AgeAnalysisService {
ResponseEntity<NameAgeEntity> getAgeAnalysisForName(String nameToAnalyze);
}

View File

@ -0,0 +1,11 @@
package com.baeldung.accesing_session_attributes.business;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import com.baeldung.accesing_session_attributes.business.entities.NameCountriesEntity;
@Service
public interface CountryAnalysisService {
ResponseEntity<NameCountriesEntity> getCountryAnalysisForName(String nameToAnalyze);
}

View File

@ -0,0 +1,11 @@
package com.baeldung.accesing_session_attributes.business;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import com.baeldung.accesing_session_attributes.business.entities.NameGenderEntity;
@Service
public interface GenderAnalysisService {
ResponseEntity<NameGenderEntity> getGenderAnalysisForName(String nameToAnalyze);
}

View File

@ -0,0 +1,15 @@
package com.baeldung.accesing_session_attributes.business;
import java.util.concurrent.CompletableFuture;
import org.springframework.stereotype.Service;
import com.baeldung.accesing_session_attributes.business.beans.NameRequest;
import com.baeldung.accesing_session_attributes.business.entities.NameAnalysisEntity;
@Service
public interface NameAnalysisService {
public NameRequest getLastNameRequest();
public CompletableFuture<NameAnalysisEntity> searchForName(NameRequest nameRequest);
}

View File

@ -0,0 +1,47 @@
package com.baeldung.accesing_session_attributes.business.beans;
public class NameRequest {
private String name;
public NameRequest() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NameRequest other = (NameRequest) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "NameRequestEntity [name=" + name + "]";
}
}

View File

@ -0,0 +1,81 @@
package com.baeldung.accesing_session_attributes.business.entities;
/**
* https://api.agify.io/?name=michael
*/
public class NameAgeEntity {
private Integer age;
private Long count;
private String name;
public NameAgeEntity() {
super();
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Long getCount() {
return count;
}
public void setCount(Long count) {
this.count = count;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((count == null) ? 0 : count.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NameAgeEntity other = (NameAgeEntity) obj;
if (age == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (count == null) {
if (other.count != null)
return false;
} else if (!count.equals(other.count))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "NameAgeEntity [age=" + age + ", count=" + count + ", name=" + name + "]";
}
}

View File

@ -0,0 +1,92 @@
package com.baeldung.accesing_session_attributes.business.entities;
public class NameAnalysisEntity {
private String name;
private NameAgeEntity age;
private NameCountriesEntity countries;
private NameGenderEntity gender;
public NameAnalysisEntity() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public NameAgeEntity getAge() {
return age;
}
public void setAge(NameAgeEntity age) {
this.age = age;
}
public NameCountriesEntity getCountries() {
return countries;
}
public void setCountries(NameCountriesEntity countries) {
this.countries = countries;
}
public NameGenderEntity getGender() {
return gender;
}
public void setGender(NameGenderEntity gender) {
this.gender = gender;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((countries == null) ? 0 : countries.hashCode());
result = prime * result + ((gender == null) ? 0 : gender.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NameAnalysisEntity other = (NameAnalysisEntity) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (age == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (countries == null) {
if (other.countries != null)
return false;
} else if (!countries.equals(other.countries))
return false;
if (gender == null) {
if (other.gender != null)
return false;
} else if (!gender.equals(other.gender))
return false;
return true;
}
@Override
public String toString() {
return "NameAnalysisEntity [name=" + name + ", age=" + age + ", countries=" + countries + ", gender=" + gender + "]";
}
}

View File

@ -0,0 +1,68 @@
package com.baeldung.accesing_session_attributes.business.entities;
import java.util.List;
/**
* https://api.nationalize.io/?name=michael
*/
public class NameCountriesEntity {
private String name;
private List<NameCountryEntity> country;
public NameCountriesEntity() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<NameCountryEntity> getCountry() {
return country;
}
public void setCountry(List<NameCountryEntity> country) {
this.country = country;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((country == null) ? 0 : country.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NameCountriesEntity other = (NameCountriesEntity) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (country == null) {
if (other.country != null)
return false;
} else if (!country.equals(other.country))
return false;
return true;
}
@Override
public String toString() {
return "NameCountriesModel [name=" + name + ", country=" + country + "]";
}
}

View File

@ -0,0 +1,72 @@
package com.baeldung.accesing_session_attributes.business.entities;
/**
* NameCountryModel
*
* https://www.countryflagicons.com/
* <img src="https://www.countryflagicons.com/STYLE/size/COUNTRYCODE.png">
* STYLE: FLAT, SHINY
* size: 16, 24, 32, 48, 64
* COUNTRYCODE: country_id
*/
public class NameCountryEntity {
private String country_id;
private Float probability;
public NameCountryEntity() {
super();
}
public String getCountry_id() {
return country_id;
}
public void setCountry_id(String country_id) {
this.country_id = country_id;
}
public Float getProbability() {
return probability;
}
public void setProbability(Float probability) {
this.probability = probability;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((country_id == null) ? 0 : country_id.hashCode());
result = prime * result + ((probability == null) ? 0 : probability.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NameCountryEntity other = (NameCountryEntity) obj;
if (country_id == null) {
if (other.country_id != null)
return false;
} else if (!country_id.equals(other.country_id))
return false;
if (probability == null) {
if (other.probability != null)
return false;
} else if (!probability.equals(other.probability))
return false;
return true;
}
@Override
public String toString() {
return "NameCountryModel [country_id=" + country_id + ", probability=" + probability + "]";
}
}

View File

@ -0,0 +1,97 @@
package com.baeldung.accesing_session_attributes.business.entities;
/**
* NameGenderModel
* https://api.genderize.io/?name=victor
*/
public class NameGenderEntity {
private Long count;
private String gender;
private String name;
private Float probability;
public NameGenderEntity() {
super();
}
public Long getCount() {
return count;
}
public void setCount(Long count) {
this.count = count;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getProbability() {
return probability;
}
public void setProbability(Float probability) {
this.probability = probability;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((count == null) ? 0 : count.hashCode());
result = prime * result + ((gender == null) ? 0 : gender.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((probability == null) ? 0 : probability.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NameGenderEntity other = (NameGenderEntity) obj;
if (count == null) {
if (other.count != null)
return false;
} else if (!count.equals(other.count))
return false;
if (gender == null) {
if (other.gender != null)
return false;
} else if (!gender.equals(other.gender))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (probability == null) {
if (other.probability != null)
return false;
} else if (!probability.equals(other.probability))
return false;
return true;
}
@Override
public String toString() {
return "NameGenderModel [count=" + count + ", gender=" + gender + ", name=" + name + ", probability=" + probability + "]";
}
}

View File

@ -0,0 +1,22 @@
package com.baeldung.accesing_session_attributes.business.entities.factories;
import org.springframework.stereotype.Service;
import com.baeldung.accesing_session_attributes.business.entities.NameAgeEntity;
import com.baeldung.accesing_session_attributes.business.entities.NameAnalysisEntity;
import com.baeldung.accesing_session_attributes.business.entities.NameCountriesEntity;
import com.baeldung.accesing_session_attributes.business.entities.NameGenderEntity;
@Service
public class NameAnalysisEntityFactory {
public NameAnalysisEntity getInstance(String nameRequest, NameGenderEntity gender, NameAgeEntity age, NameCountriesEntity countries) {
NameAnalysisEntity nameAnalysis = new NameAnalysisEntity();
nameAnalysis.setName(nameRequest);
nameAnalysis.setGender(gender);
nameAnalysis.setAge(age);
nameAnalysis.setCountries(countries);
return nameAnalysis;
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.accesing_session_attributes.business.impl;
import java.net.URI;
import java.text.MessageFormat;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.baeldung.accesing_session_attributes.business.AgeAnalysisService;
import com.baeldung.accesing_session_attributes.business.entities.NameAgeEntity;
@Component
public class AgeAnalysisServiceImpl implements AgeAnalysisService {
@Value("${name-analysis-controller.name-age-api-url:https://api.agify.io/?name={0}}")
private String nameAgeApiUrl;
@Override
public ResponseEntity<NameAgeEntity> getAgeAnalysisForName(String nameToAnalyze) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForEntity(URI.create(MessageFormat.format(nameAgeApiUrl, nameToAnalyze)), NameAgeEntity.class);
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.accesing_session_attributes.business.impl;
import java.net.URI;
import java.text.MessageFormat;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.baeldung.accesing_session_attributes.business.CountryAnalysisService;
import com.baeldung.accesing_session_attributes.business.entities.NameCountriesEntity;
@Component
public class CountryAnalysisServiceImpl implements CountryAnalysisService {
@Value("${name-analysis-controller.name-countries-api-url:https://api.nationalize.io/?name={0}}")
private String nameCountriesApiUrl;
@Override
public ResponseEntity<NameCountriesEntity> getCountryAnalysisForName(String nameToAnalyze) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForEntity(URI.create(MessageFormat.format(nameCountriesApiUrl, nameToAnalyze)), NameCountriesEntity.class);
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.accesing_session_attributes.business.impl;
import java.net.URI;
import java.text.MessageFormat;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.baeldung.accesing_session_attributes.business.GenderAnalysisService;
import com.baeldung.accesing_session_attributes.business.entities.NameGenderEntity;
@Component
public class GenderAnalysisServiceImpl implements GenderAnalysisService {
@Value("${name-analysis-controller.name-gender-api-url:https://api.genderize.io/?name={0}}")
private String nameGenderApiUrl;
@Override
public ResponseEntity<NameGenderEntity> getGenderAnalysisForName(String nameToAnalyze) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForEntity(URI.create(MessageFormat.format(nameGenderApiUrl, nameToAnalyze)), NameGenderEntity.class);
}
}

View File

@ -0,0 +1,68 @@
package com.baeldung.accesing_session_attributes.business.impl;
import java.net.URLEncoder;
import java.util.concurrent.CompletableFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import com.baeldung.accesing_session_attributes.business.AgeAnalysisService;
import com.baeldung.accesing_session_attributes.business.CountryAnalysisService;
import com.baeldung.accesing_session_attributes.business.GenderAnalysisService;
import com.baeldung.accesing_session_attributes.business.NameAnalysisService;
import com.baeldung.accesing_session_attributes.business.beans.NameRequest;
import com.baeldung.accesing_session_attributes.business.entities.NameAgeEntity;
import com.baeldung.accesing_session_attributes.business.entities.NameAnalysisEntity;
import com.baeldung.accesing_session_attributes.business.entities.NameCountriesEntity;
import com.baeldung.accesing_session_attributes.business.entities.NameGenderEntity;
import com.baeldung.accesing_session_attributes.business.entities.factories.NameAnalysisEntityFactory;
@Component
public class NameAnalysisServiceImpl implements NameAnalysisService {
private NameRequest lastNameRequest;
private AgeAnalysisService ageAnalysisService;
private CountryAnalysisService countryAnalysisService;
private GenderAnalysisService genderAnalysisService;
private NameAnalysisEntityFactory nameAnalysisEntityFactory;
@Autowired
public NameAnalysisServiceImpl(AgeAnalysisService ageAnalysisService, CountryAnalysisService countryAnalysisService, GenderAnalysisService genderAnalysisService, NameAnalysisEntityFactory nameAnalysisEntityFactory) {
super();
this.ageAnalysisService = ageAnalysisService;
this.countryAnalysisService = countryAnalysisService;
this.genderAnalysisService = genderAnalysisService;
this.nameAnalysisEntityFactory = nameAnalysisEntityFactory;
lastNameRequest = new NameRequest();
lastNameRequest.setName("Rigoberto");
}
public NameRequest getLastNameRequest() {
return lastNameRequest;
}
public CompletableFuture<NameAnalysisEntity> searchForName(NameRequest nameRequest) {
this.lastNameRequest.setName(nameRequest.getName());
return analyzeName();
}
@Async
private CompletableFuture<NameAnalysisEntity> analyzeName() {
try {
String nameToAnalyze = URLEncoder.encode(lastNameRequest.getName(), "UTF-8");
ResponseEntity<NameAgeEntity> ageRequestResponse = ageAnalysisService.getAgeAnalysisForName(nameToAnalyze);
ResponseEntity<NameCountriesEntity> countriesRequestResponse = countryAnalysisService.getCountryAnalysisForName(nameToAnalyze);
ResponseEntity<NameGenderEntity> genderRequestResponse = genderAnalysisService.getGenderAnalysisForName(nameToAnalyze);
NameAnalysisEntity nameAnalysis = nameAnalysisEntityFactory.getInstance(lastNameRequest.getName(), genderRequestResponse.getBody(), ageRequestResponse.getBody(), countriesRequestResponse.getBody());
return CompletableFuture.completedFuture(nameAnalysis);
} catch (Exception e) {
return CompletableFuture.failedFuture(e);
}
}
}

View File

@ -0,0 +1,86 @@
package com.baeldung.accesing_session_attributes.web;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring6.SpringTemplateEngine;
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring6.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.web.servlet.JakartaServletWebApplication;
@Configuration
@EnableWebMvc
public class SpringWebConfig implements WebMvcConfigurer, ApplicationContextAware {
private WebApplicationContext webApplicationContext;
public SpringWebConfig() {
super();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.webApplicationContext = (WebApplicationContext) applicationContext;
}
/*
* Dispatcher configuration for serving static resources
*/
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry);
registry.addResourceHandler("/images/**")
.addResourceLocations("/images/");
registry.addResourceHandler("/css/**")
.addResourceLocations("/css/");
registry.addResourceHandler("/js/**")
.addResourceLocations("/js/");
}
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.addBasenames("name-analysis");
return messageSource;
}
@Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(this.webApplicationContext);
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCacheable(true);
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setMessageSource(webApplicationContext);
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
@Bean
public ThymeleafViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
@Bean
public JakartaServletWebApplication jakartaServletWebApplication() {
return JakartaServletWebApplication.buildApplication(webApplicationContext.getServletContext());
}
}

View File

@ -0,0 +1,67 @@
package com.baeldung.accesing_session_attributes.web.beans;
import java.util.Date;
import com.baeldung.accesing_session_attributes.business.beans.NameRequest;
public class SessionNameRequest {
private Date requestDate;
private NameRequest nameRequest;
public SessionNameRequest() {
super();
}
public Date getRequestDate() {
return requestDate;
}
public void setRequestDate(Date requestDate) {
this.requestDate = requestDate;
}
public NameRequest getNameRequest() {
return nameRequest;
}
public void setNameRequest(NameRequest nameRequest) {
this.nameRequest = nameRequest;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((requestDate == null) ? 0 : requestDate.hashCode());
result = prime * result + ((nameRequest == null) ? 0 : nameRequest.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SessionNameRequest other = (SessionNameRequest) obj;
if (requestDate == null) {
if (other.requestDate != null)
return false;
} else if (!requestDate.equals(other.requestDate))
return false;
if (nameRequest == null) {
if (other.nameRequest != null)
return false;
} else if (!nameRequest.equals(other.nameRequest))
return false;
return true;
}
@Override
public String toString() {
return "SessionNameRequest [requestDate=" + requestDate + ", nameRequest=" + nameRequest + "]";
}
}

View File

@ -0,0 +1,143 @@
package com.baeldung.accesing_session_attributes.web.controllers;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.thymeleaf.web.IWebSession;
import org.thymeleaf.web.servlet.IServletWebExchange;
import org.thymeleaf.web.servlet.JakartaServletWebApplication;
import com.baeldung.accesing_session_attributes.business.NameAnalysisService;
import com.baeldung.accesing_session_attributes.business.beans.NameRequest;
import com.baeldung.accesing_session_attributes.business.entities.NameAnalysisEntity;
import com.baeldung.accesing_session_attributes.web.beans.SessionNameRequest;
import com.baeldung.accesing_session_attributes.web.factories.SessionNameRequestFactory;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@Controller
public class NameAnalysisController {
private NameAnalysisService nameAnalysisService;
private HttpServletRequest request;
private SessionNameRequestFactory sessionNameRequestFactory;
private HttpServletResponse response;
private JakartaServletWebApplication jakartaServletWebApplication;
@Autowired
public NameAnalysisController(JakartaServletWebApplication jakartaServletWebApplication, HttpServletRequest request, HttpServletResponse response, NameAnalysisService nameAnalysisService, SessionNameRequestFactory sessionNameRequestFactory) {
super();
this.jakartaServletWebApplication = jakartaServletWebApplication;
this.request = request;
this.response = response;
this.nameAnalysisService = nameAnalysisService;
this.sessionNameRequestFactory = sessionNameRequestFactory;
}
@ModelAttribute("nameRequest")
public NameRequest nameRequest() {
return nameAnalysisService.getLastNameRequest();
}
@RequestMapping({ "/", "/name-analysis" })
public String showNameAnalysis() {
return "name-analysis";
}
@RequestMapping(value = "/name-analysis", params = { "search" })
public String performNameAnalysis(final NameRequest nameRequest, final BindingResult bindingResult) {
performNameRequest(nameRequest);
return "name-analysis";
}
@RequestMapping(value = "/name-analysis/clear")
public String clearNameAnalysis() {
clearAnalysis();
return "redirect:/name-analysis";
}
@RequestMapping(value = "/name-analysis/remove-history-request", params = { "id" })
public String removeRequest() {
try {
final Integer rowId = Integer.valueOf(request.getParameter("id"));
removeRequest(rowId);
} catch (Exception e) {
e.printStackTrace();
}
return "redirect:/name-analysis";
}
private void removeRequest(Integer rowId) {
IWebSession session = getIWebSession();
Object requests = session.getAttributeValue("requests");
if (rowId != null && requests != null && (requests instanceof List)) {
((List<SessionNameRequest>) requests).remove(rowId.intValue());
}
}
private void performNameRequest(final NameRequest nameRequest) {
try {
CompletableFuture<NameAnalysisEntity> nameAnalysis = this.nameAnalysisService.searchForName(nameRequest);
NameAnalysisEntity nameAnalysisEntity = nameAnalysis.get(30, TimeUnit.SECONDS);
sessionRegisterRequest(nameRequest);
sessionRegisterAnalysis(nameAnalysisEntity);
sessionClearAnalysisError();
} catch (Exception e) {
e.printStackTrace();
sessionSetAnalysisError(nameRequest);
}
}
private void sessionClearAnalysisError() {
IWebSession session = getIWebSession();
session.setAttributeValue("analysisError", null);
}
private void sessionSetAnalysisError(NameRequest nameRequest) {
IWebSession session = getIWebSession();
session.setAttributeValue("analysisError", nameRequest);
}
private void clearAnalysis() {
IWebSession session = getIWebSession();
session.setAttributeValue("lastAnalysis", null);
}
private void sessionRegisterAnalysis(NameAnalysisEntity analysis) {
IWebSession session = getIWebSession();
session.setAttributeValue("lastAnalysis", analysis);
}
private void sessionRegisterRequest(NameRequest nameRequest) {
IWebSession session = getIWebSession();
session.setAttributeValue("lastRequest", nameRequest);
SessionNameRequest sessionNameRequest = sessionNameRequestFactory.getInstance(nameRequest);
List<SessionNameRequest> requests = getRequestsFromSession(session);
requests.add(0, sessionNameRequest);
}
private List<SessionNameRequest> getRequestsFromSession(IWebSession session) {
Object requests = session.getAttributeValue("requests");
if (requests == null || !(requests instanceof List)) {
List<SessionNameRequest> sessionNameRequests = new ArrayList<>();
session.setAttributeValue("requests", sessionNameRequests);
requests = sessionNameRequests;
}
return (List<SessionNameRequest>) requests;
}
private IWebSession getIWebSession() {
IServletWebExchange webExchange = this.jakartaServletWebApplication.buildExchange(request, response);
return webExchange.getSession();
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.accesing_session_attributes.web.factories;
import java.util.Date;
import org.springframework.stereotype.Service;
import com.baeldung.accesing_session_attributes.business.beans.NameRequest;
import com.baeldung.accesing_session_attributes.web.beans.SessionNameRequest;
@Service
public class SessionNameRequestFactory {
public SessionNameRequest getInstance(NameRequest nameRequest) {
NameRequest cloned = new NameRequest();
cloned.setName(nameRequest.getName());
SessionNameRequest sessionNameRequest = new SessionNameRequest();
sessionNameRequest.setRequestDate(new Date());
sessionNameRequest.setNameRequest(cloned);
return sessionNameRequest;
}
}

View File

@ -0,0 +1,3 @@
spring.http.encoding.enabled=true
spring.devtools.add-properties=false
logging.level.web=DEBUG

View File

@ -0,0 +1,20 @@
history.header.actions=Actions
history.header.date=Date
history.header.name=Name
history.no-data=Without data
history.request.remove-row=Remove row
history.title=Requests History
name-analysis.form.button.analyze=Analyze
name-analysis.form.name=Name
name-analysis.title=Analyze name
result.clear=Borrar
result.column.age-count=Number of persons
result.column.age=Age
result.column.city=City
result.column.gender=Gender
result.column.name=Name
result.error=Error during analysis
result.header.value=Value
result.no-request-performed=Without data
result.title=Name analyzed
web.title=Baeldung - Accessing Session Attributes in Thymeleaf

View File

@ -0,0 +1,20 @@
history.header.actions=Actions
history.header.date=Date
history.header.name=Name
history.no-data=Without data
history.request.remove-row=Remove row
history.title=Requests History
name-analysis.form.button.analyze=Analyze
name-analysis.form.name=Name
name-analysis.title=Analyze name
result.clear=Borrar
result.column.age-count=Number of persons
result.column.age=Age
result.column.city=City
result.column.gender=Gender
result.column.name=Name
result.error=Error during analysis
result.header.value=Value
result.no-request-performed=Without data
result.title=Name analyzed
web.title=Baeldung - Accessing Session Attributes in Thymeleaf

View File

@ -0,0 +1,20 @@
history.header.actions=Acciones
history.header.date=Fecha
history.header.name=Nombre
history.no-data=Sin datos
history.request.remove-row=Eliminar fila
history.title=Historial
name-analysis.form.button.analyze=Analizar
name-analysis.form.name=Nombre
name-analysis.title=Analizar nombre
result.clear=Borrar
result.column.age-count=Ocurrencias
result.column.age=Edad
result.column.city=Ciudad
result.column.gender=Género
result.column.name=Nombre
result.error=Se produjo un error
result.header.value=Valor
result.no-request-performed=Sin datos
result.title=Nombre analizado
web.title=Baeldung - Acceso a los atributos de la sesión con Thymeleaf

View File

@ -0,0 +1,125 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title data-th-text="#{web.title}">Baeldung - Accessing Session Attributes in Thymeleaf</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" media="all" />
<link rel="stylesheet" href="css/bootstrap.min.css" />
</head>
<body class="full-container px-4">
<div class="row py-5">
<div class="col-5 px-4">
<div class="border mx-auto">
<h5 class="display-5 text-center mb-3" data-th-text="#{name-analysis.title}">Analizar nombre</h5>
<div class="border-top py-4">
<form class="px-4 mb-3" action="#" data-th-action="@{/name-analysis}" data-th-object="${nameRequest}"
method="post">
<div class="border rounded-3 px-4 py-4 bg-light">
<div class="col-12 mb-3">
<label for="name" class="form-label" data-th-text="#{name-analysis.form.name}">Nombre</label>
<input id="name" type="text" class="form-control" data-th-field="*{name}" placeholder value required>
</div>
<div class="row mb-3">
<div class="btn-toolbar" role="toolbar">
<div class="btn-group me-2">
<button class="btn btn-lg btn-primary" type="submit" name="search"
data-th-text="#{name-analysis.form.button.analyze}">Analizar</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
<hr class="my-4" />
<div class="border mx-auto">
<h5 class="display-5 text-center mb-3" data-th-text="#{result.title}">Nombre analizado</h5>
<div class="border-top py-4">
<h4 class="display-6 text-center" data-th-if="${#ctx.session.analysisError}!=null"
data-th-text="#{result.error}">Error durante el análisis</h4>
<h4 class="display-6 text-center"
data-th-if="${#ctx.session.analysisError}==null and ${#ctx.session.lastAnalysis}==null"
data-th-text="#{result.no-request-performed}">Sin búsquedas</h4>
<div data-th-if="${#ctx.session.analysisError}==null and ${#ctx.session.lastAnalysis}!=null"
data-th-object="${#ctx.session.lastAnalysis}">
<table class="table">
<thead>
<tr>
<th class="px-2" scope="col">#</th>
<th class="px-2" scope="col" data-th-text="#{result.header.value}">Valor</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" data-th-text="#{result.column.name}">Nombre</th>
<td><span data-th-text="*{name}">Nombre buscado</span></td>
</tr>
<tr>
<th scope="row" data-th-text="#{result.column.gender}">Género</th>
<td><span data-th-text="*{gender.gender}">[Masculino|Femenino]</span> (<span
data-th-text="*{gender.probability}">{%}</span>)</td>
</tr>
<tr>
<th scope="row" data-th-text="#{result.column.age}">Edad</th>
<td><span data-th-text="*{age.age}">{Edad media del nombre}</span></td>
</tr>
<tr>
<th scope="row" data-th-text="#{result.column.age-count}">Número de personas<h>
<td><span data-th-text="*{age.count}">{Número de personas}</span></td>
</tr>
<tr data-th-each="country : *{countries.country}">
<th scope="row" data-th-text="#{result.column.city}">Ciudad</th>
<td><span data-th-text="${country.country_id}">Country id</span> (<span
data-th-text="${country.probability}">{%}</span>)</td>
</tr>
</tbody>
</table>
<div class="px-2">
<a class="btn btn-lg btn-danger px-2" data-th-href="@{/name-analysis/clear}"
data-th-text="#{result.clear}">Borrar</a>
</div>
</div>
</div>
</div>
</div>
<div class="col-7">
<div class="border mx-auto">
<h5 class="display-5 text-center mb-3" data-th-text="#{history.title}">Historial</h5>
<div class="border-top py-4">
<h4 class="display-6 text-center"
data-th-if="${#ctx.session.requests}==null or ${#ctx.session.requests.isEmpty()}"
data-th-text="#{history.no-data}">Sin datos</h4>
<table data-th-if="${#ctx.session.requests}!=null and ${not #ctx.session.requests.isEmpty()}">
<thead>
<tr>
<th class="px-2" data-th-text="#{history.header.date}">Fecha</th>
<th class="px-2" data-th-text="#{history.header.name}">Nombre</th>
<th class="px-2" data-th-text="#{history.header.actions}">Acciones</th>
</tr>
</thead>
<tbody>
<tr data-th-each="request,rowStat : ${#ctx.session.requests}">
<td class="px-2">[[${request.requestDate}]]</td>
<td class="px-2">[[${request.nameRequest}]]</td>
<td class="px-2">
<a class="btn btn-danger px-2" name="remove-request"
data-th-href="@{/name-analysis/remove-history-request(id=${rowStat.index})}"
data-th-text="#{history.request.remove-row}">Remove row</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,107 @@
package com.baeldung.accesing_session_attributes.business.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.baeldung.accesing_session_attributes.business.AgeAnalysisService;
import com.baeldung.accesing_session_attributes.business.CountryAnalysisService;
import com.baeldung.accesing_session_attributes.business.GenderAnalysisService;
import com.baeldung.accesing_session_attributes.business.NameAnalysisService;
import com.baeldung.accesing_session_attributes.business.beans.NameRequest;
import com.baeldung.accesing_session_attributes.business.entities.NameAgeEntity;
import com.baeldung.accesing_session_attributes.business.entities.NameAnalysisEntity;
import com.baeldung.accesing_session_attributes.business.entities.NameCountriesEntity;
import com.baeldung.accesing_session_attributes.business.entities.NameGenderEntity;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@ExtendWith(SpringExtension.class)
@SpringBootTest
@ActiveProfiles({ "test" })
public class NameAnalysisServiceImplUnitTest {
@Autowired
private ObjectMapper om;
@MockBean
private AgeAnalysisService ageAnalysisService;
@MockBean
private CountryAnalysisService countryAnalysisService;
@MockBean
private GenderAnalysisService genderAnalysisService;
@Autowired
private NameAnalysisService toTest;
@Test
void givenRigoberto_whenCallCompletesOk_thenNameAnalysisEntityReceived()
throws JsonMappingException, JsonProcessingException {
String nameToAnalyze = "Rigoberto";
NameAgeEntity rigobertoAgeAnalysis = om.readValue("{\"age\":68,\"count\":683,\"name\":\"Rigoberto\"}",
NameAgeEntity.class);
NameCountriesEntity rigobertoCountriesAnalysis = om.readValue(
"{\"country\":[{\"country_id\":\"MX\",\"probability\":0.132},{\"country_id\":\"PA\",\"probability\":0.107},{\"country_id\":\"CR\",\"probability\":0.09},{\"country_id\":\"HN\",\"probability\":0.082},{\"country_id\":\"GT\",\"probability\":0.075}],\"name\":\"Rigoberto\"}",
NameCountriesEntity.class);
NameGenderEntity rigobertoGenderAnalysis = om.readValue(
"{\"count\":13927,\"gender\":\"male\",\"name\":\"Rigoberto\",\"probability\":1.0}",
NameGenderEntity.class);
Mockito.when(ageAnalysisService.getAgeAnalysisForName(nameToAnalyze))
.thenReturn(ResponseEntity.ok(rigobertoAgeAnalysis));
Mockito.when(countryAnalysisService.getCountryAnalysisForName(nameToAnalyze))
.thenReturn(ResponseEntity.ok(rigobertoCountriesAnalysis));
Mockito.when(genderAnalysisService.getGenderAnalysisForName(nameToAnalyze))
.thenReturn(ResponseEntity.ok(rigobertoGenderAnalysis));
NameRequest rigobertoRequest = new NameRequest();
rigobertoRequest.setName(nameToAnalyze);
CompletableFuture<NameAnalysisEntity> result = toTest.searchForName(rigobertoRequest);
try {
NameAnalysisEntity analysisResult = result.get();
assertEquals(nameToAnalyze, analysisResult.getName());
assertEquals(rigobertoAgeAnalysis, analysisResult.getAge());
assertEquals(rigobertoCountriesAnalysis, analysisResult.getCountries());
assertEquals(rigobertoGenderAnalysis, analysisResult.getGender());
} catch (Exception e) {
fail("Not expected exception");
}
}
@Test
void whenNameAnalysisRequestFails_thenCompletableFutureException() {
String nameToAnalyze = "Rigoberto";
Mockito.when(ageAnalysisService.getAgeAnalysisForName(nameToAnalyze))
.thenThrow(new RuntimeException("Failed age analysis for name"));
Mockito.when(countryAnalysisService.getCountryAnalysisForName(nameToAnalyze))
.thenThrow(new RuntimeException("Failed country analysis for name"));
Mockito.when(genderAnalysisService.getGenderAnalysisForName(nameToAnalyze))
.thenThrow(new RuntimeException("Failed gender analysis for name"));
NameRequest rigobertoRequest = new NameRequest();
rigobertoRequest.setName(nameToAnalyze);
CompletableFuture<NameAnalysisEntity> result = toTest.searchForName(rigobertoRequest);
try {
result.get();
fail("Expected Execution Exception");
} catch (ExecutionException e) {
} catch (InterruptedException e) {
fail("Expected Execution Exception");
}
}
}

View File

@ -0,0 +1,4 @@
spring.http.encoding.enabled=true
spring.devtools.add-properties=false
logging.level.web=DEBUG
spring.thymeleaf.check-template-location=false

View File

@ -0,0 +1,29 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung.spring-thymeleaf-attributes</groupId>
<artifactId>spring-thymeleaf-attributes-modules</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-3</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
<modules>
<module>accessing-session-attributes</module>
</modules>
</project>