BAEL-5575 (#14016)
* 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:
parent
50faedd2bc
commit
9fb2b4d919
|
@ -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
|
|
@ -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>
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 + "]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 + "]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
spring.http.encoding.enabled=true
|
||||||
|
spring.devtools.add-properties=false
|
||||||
|
logging.level.web=DEBUG
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
File diff suppressed because one or more lines are too long
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
spring.http.encoding.enabled=true
|
||||||
|
spring.devtools.add-properties=false
|
||||||
|
logging.level.web=DEBUG
|
||||||
|
spring.thymeleaf.check-template-location=false
|
|
@ -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>
|
Loading…
Reference in New Issue