Commit c28a429f by jhrabal

Initial commit

parents
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
.classpath
.factorypath
.project
.apt_generated
.settings
target
node_modules
/bin/
/tmp/
/src/main/resources/resources/app.css
/src/main/resources/resources/app.js
boot-react-common/.classpath
boot-react-common/.factorypath
boot-react-common/.project
boot-react-common/.apt_generated
boot-react-common/.settings
boot-react-common/target
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jh</groupId>
<artifactId>jh-boot</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.date>${maven.build.timestamp}</project.build.date>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
<project.build.date>${maven.build.timestamp}</project.build.date>
<java.version>8</java.version>
</properties>
<dependencies>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<filtering>false</filtering>
<directory>${basedir}/src/main/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<filtering>false</filtering>
<directory>${basedir}/src/main/resources</directory>
</resource>
<resource>
<filtering>true</filtering>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>*.properties</include>
</includes>
</resource>
</resources>
<testResources>
<testResource>
<filtering>false</filtering>
<directory>${basedir}/src/test/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
<testResource>
<filtering>false</filtering>
<directory>${basedir}/src/test/resources</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.4.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<sourcepath>${basedir}/dummy</sourcepath>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>${java.version}</release>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>com.springsource.repository.maven.release</id>
<url>http://repo.springsource.org/release/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>com.springsource.repository.bundles.release</id>
<url>http://repository.springsource.com/maven/bundles/release</url>
</repository>
<repository>
<id>com.springsource.repository.bundles.external</id>
<url>http://repository.springsource.com/maven/bundles/external</url>
</repository>
</repositories>
</project>
package com.jh.common.domain;
public enum DateType {
BILLED_DATE("billedDate"),
DUE_DATE("dueDate");
private String column;
private DateType(String dateType) {
this.column = dateType;
}
public String getColumn() {
return column;
}
}
package com.jh.common.domain;
public class NamedValue<T> {
/** The name. */
private String name;
/** The value. */
private T value;
/** The id. */
private Long id;
/**
* Instantiates a new named value.
*
* @param name the name
* @param value the value
*/
public NamedValue(String name, T value) {
super();
this.name = name;
this.value = value;
}
/**
* Instantiates a new named value.
*
* @param id the id
* @param name the name
* @param value the value
*/
public NamedValue(Long id, String name, T value) {
super();
this.id = id;
this.name = name;
this.value = value;
}
/**
* Gets the name.
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Gets the value.
*
* @return the value
*/
public T getValue() {
return value;
}
/**
* Gets the id.
*
* @return the id
*/
public Long getId() {
return id;
}
}
\ No newline at end of file
package com.jh.common.jpa;
/**
* The Class AbstractHibernateConfiguration.
*/
public class AbstractHibernateConfiguration {
}
package com.jh.common.jpa;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.jpa.HibernateEntityManager;
import org.hibernate.query.NativeQuery;
import org.hibernate.transform.Transformers;
import com.jh.common.web.list.Page;
import com.jh.common.web.list.PagingInfo;
import com.jh.common.web.list.SortTrend;
import com.jh.common.web.list.SortingInfo;
/**
* TODO comment.
*
* @author jh
*/
public abstract class AbstractHibernateRepository {
/** The session factory. */
@PersistenceContext
private EntityManager entityManager;
/**
* Gets the session.
*
* @return the session
*/
protected Session getSession() {
return ((HibernateEntityManager) entityManager).getSession();
}
/**
* Criteria.
*
* @param clazz the clazz
* @return the criteria
*/
protected Criteria criteria(Class<?> clazz) {
return getSession().createCriteria(clazz);
}
@SuppressWarnings("unchecked")
protected <T> NativeQuery<T> nativeQuery(String query) {
return (NativeQuery<T>) entityManager.createNativeQuery(query);
}
/**
* TODO comment
*
*
* @param tokens the tokens
* @param fields the fields
* @return the conjunction
*/
protected Conjunction fulltextCriteria(List<String> tokens, String...fields) {
Objects.requireNonNull(tokens, "Tokens must not be null");
Objects.requireNonNull(fields, "Fields must not be null");
Conjunction and = Restrictions.conjunction();
for (String t : tokens) {
Disjunction d = Restrictions.disjunction();
for (String f : fields) {
d.add(Restrictions.ilike(f, t, MatchMode.ANYWHERE));
}
and.add(d);
}
return and;
}
/**
* TODO comment.
*
* @param c the c
* @param clazz the clazz
* @param properties the properties
*/
protected void fields(Criteria c, Class<?> clazz, String...properties) {
Objects.requireNonNull(c, "Criteria must not be null");
Objects.requireNonNull(clazz, "Class must not be null");
if (properties == null || properties.length == 0) {
return;
}
ProjectionList pl = Projections.projectionList();
for (String p : properties) {
pl.add(Projections.alias(Projections.property(p), p));
}
c.setProjection(pl);
c.setResultTransformer(Transformers.aliasToBean(clazz));
}
/**
* TODO comment.
*
* @param <T> the generic type
* @param c the c
* @param pagingInfo the paging info
* @return the page
*/
@SuppressWarnings("unchecked")
protected <T> Page<T> pagedResult(Criteria c, PagingInfo pagingInfo) {
if (pagingInfo == null) {
return new Page<>(c.list());
} else if (pagingInfo.getPageSize() < 1) {
return new Page<>(Collections.emptyList());
}
int pageSize = pagingInfo.getPageSize();
int count = ((Number) c.setProjection(Projections.rowCount()).uniqueResult()).intValue();
//count pages count
int pagesCount = count / pageSize + ( count % pageSize == 0 ? 0 : 1);
//limit results
c.setFirstResult(pageSize * pagingInfo.getPage());
c.setMaxResults(pageSize);
//reset criteria https://stackoverflow.com/a/1472958
c.setProjection(null);
c.setResultTransformer(Criteria.ROOT_ENTITY);
sortedCriteria(c, pagingInfo);
return new Page<>(pagingInfo.getPage(), pagesCount, c.list(), pagingInfo.getField(), pagingInfo.getTrend());
}
/**
* Sorted criteria.
*
* @param c the c
* @param sortingInfo the sorting info
* @return the criteria
*/
protected Criteria sortedCriteria(Criteria c, SortingInfo sortingInfo) {
if (sortingInfo != null && sortingInfo.getField() != null) {
c.addOrder(sortingInfo.getTrend() == SortTrend.DESCENDING ? Order.desc(sortingInfo.getField()) : Order.asc(sortingInfo.getField()));
}
return c;
}
/**
* TODO.
*
* @param <T> the generic type
* @param entity the entity
*/
public <T extends AbstractIdEntity> void save(T entity) {
if (entity.getId() == null) {
getSession().save(entity);
} else {
getSession().update(entity);
}
}
/**
* Delete.
*
* @param <T> the generic type
* @param entity the entity
*/
public <T extends AbstractIdEntity> void delete(T entity) {
if (entity.getId() != null) {
getSession().delete(entity);
}
}
/**
* Sync references.
*
* @param <T> the generic type
* @param <S> the generic type
* @param entity the entity
* @param referencedClass the referenced class
* @param referencedField the referenced field
* @param references the references
*/
//FIXME??
protected <T extends AbstractIdEntity, S extends AbstractIdEntity> void syncReferences(T entity, String referencedClass, String referencedField, Collection<S> references) {
if (entity.getId() == null) {
//nothing to do
return;
}
//filter only already stored references
Set<S> elements = new HashSet<>();
if (references != null) {
references.stream().filter(e -> e.getId() != null).forEach(e -> elements.add(e));
}
StringBuilder sb = new StringBuilder("delete from ");
sb.append(referencedClass).append(" ref");
sb.append(" where ref.").append(referencedField).append(" = :root");
if (!elements.isEmpty()) {
sb.append(" and ref not in (:refs)");
}
Query q = getSession().createQuery(sb.toString());
q.setParameter("root", entity);
if (!elements.isEmpty()) {
q.setParameterList("refs", elements);
}
q.executeUpdate();
if (references != null) {
for (S s : references) {
save(s);
}
}
}
/**
* Sync references.
*
* @param <T> the generic type
* @param <S> the generic type
* @param entities the entities
* @param referencedClass the referenced class
* @param referencedField the referenced field
* @param valueProviders the value providers
*/
protected <T extends AbstractIdEntity, S extends AbstractIdEntity> void syncReferences(Collection<T> entities, String referencedClass, String referencedField, Function<T, ? extends Collection<S>> valueProviders) {
if (entities == null || entities.isEmpty()) {
//nothing to do
return;
}
for (T entity : entities) {
if (entity.getId() == null) {
continue;
}
syncReferences(entity, referencedClass, referencedField, valueProviders.apply(entity));
}
}
// @SuppressWarnings("unchecked")
// public <T extends AbstractIdEntity> T merge(T entity) {
// return (T) getSession().merge(entity);
// }
}
package com.jh.common.jpa;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.SequenceGenerator;
/**
* The Class AbstractIdEntity.
*/
@MappedSuperclass
public abstract class AbstractIdEntity implements Serializable {
private static final long serialVersionUID = 1L;
/** The id. */
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idGenerator")
@SequenceGenerator(name = "idGenerator", sequenceName = "ID_GENERATOR", allocationSize = 1)
@Column(name = "ID", updatable = false, nullable = false)
protected Long id;
// public AbstractIdEntity() {
// super();
// }
//
// public AbstractIdEntity(Long id) {
// this();
// setId(id);
// }
/**
* @return
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
if (id == null) {
return super.hashCode();
}
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
/**
* @param obj
* @return
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (id == null) {
return super.equals(obj);
}
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AbstractIdEntity other = (AbstractIdEntity) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
/**
* Gets the id.
*
* @return the id
*/
public Long getId() {
return id;
}
/**
* Sets the id.
*
* @param id the new id
*/
public void setId(Long id) {
this.id = id;
}
}
package com.jh.common.jpa;
import java.time.LocalDate;
import java.sql.Date;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/**
* The Class LocalDateConverter.
*/
@Converter(autoApply = true)
public class LocalDateConverter implements AttributeConverter<LocalDate, Date> {
/**
* @param locDate
* @return
* @see javax.persistence.AttributeConverter#convertToDatabaseColumn(java.lang.Object)
*/
@Override
public Date convertToDatabaseColumn(LocalDate locDate) {
return (locDate == null ? null : Date.valueOf(locDate));
}
/**
* @param sqlDate
* @return
* @see javax.persistence.AttributeConverter#convertToEntityAttribute(java.lang.Object)
*/
@Override
public LocalDate convertToEntityAttribute(Date sqlDate) {
return (sqlDate == null ? null : sqlDate.toLocalDate());
}
}
\ No newline at end of file
/*
* Created on 17. 3. 2018 21:20:00
*
*/
package com.jh.common.resource;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Function;
/**
* The Interface ResourceLoader.
*
* @author Jan Hrabal
*/
public interface ResourceLoader {
/**
* Gets the string.
*
* @param resourceName the resource name
* @param locale the locale
* @return the string
*/
String getString(String resourceName, Locale locale);
/**
* Gets the string.
*
* @param resourceName the resource name
* @return the string
*/
String getString(String resourceName);
/**
* Resolve resource name.
*
* @param templateName the template name
* @param locale the locale
* @param existenceValidator the existence validator
* @return the string
*/
static String resolveResourceName(String templateName, Locale locale, Function<String, Boolean> existenceValidator) {
Objects.requireNonNull(existenceValidator, "Provide non-null existence validator");
String extension = null;
String root = templateName;
int i = templateName.lastIndexOf('.');
int p = Math.max(templateName.lastIndexOf('/'), templateName.lastIndexOf('\\'));
if (i > p) {
extension = templateName.substring(i+1);
root = templateName.substring(0, i);
}
StringBuilder sb = null;
String country = locale.getCountry();
String lang = locale.getLanguage();
sb = new StringBuilder(root);
sb.append("_");
sb.append(lang);
sb.append("-");
sb.append(country);
if (extension != null) {
sb.append(".").append(extension);
}
String s = sb.toString();
if (existenceValidator.apply(s)) {
return s;
}
sb = new StringBuilder(root);
sb.append("_");
sb.append(lang);
if (extension != null) {
sb.append(".").append(extension);
}
s = sb.toString();
if (existenceValidator.apply(s)) {
return s;
}
//default
return templateName;
};
}
package com.jh.common.security;
import java.util.List;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import com.jh.common.security.controller.AuthError;
/**
* TODO.
*
* @author jh
*/
public interface AuthService {
/**
* Authenticate.
*
* @param username the username
* @param password the password
* @return the authentication
* @throws BadCredentialsException the bad credentials exception
*/
Authentication authenticate(String username, String password) throws BadCredentialsException;
/**
* Register.
*
* @param signup the signup
* @return the list
*/
List<AuthError> register(Signup signup);
/**
* This method is responsible for creating new reset password request
* for provided login (or should do nothing if the login is unknown).
*
* <p>The way of letting the user know the token is up to the implementation</p>
*
* @param login
*/
void generateResetToken(String login);
/**
* TODO
* @param resetPassword
*
* @return
*/
List<AuthError> resetPassword(ResetPassword resetPassword);
}
/*
* Created on 27. 7. 2017 14:37:22
*
*/
package com.jh.common.security;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* The Class JsonAuthenticationFilter.
*
* @author Jan Hrabal
*/
public class JsonAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
/** The object mapper. */
/* Default object mapper */
private ObjectMapper objectMapper = new ObjectMapper();
/**
* @param request
* @param response
* @return
* @see org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
LoginRequest loginRequest = readLoginRequest(request);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
* Read login request.
*
* @param request the request
* @return the login request
*/
protected LoginRequest readLoginRequest(HttpServletRequest request) {
try {
return objectMapper.readValue(request.getReader(), LoginRequest.class);
} catch (IOException e) {
throw new RuntimeException("Cannot parse login request", e);
}
}
/**
* Sets the object mapper.
*
* @param objectMapper the new object mapper
*/
@Autowired(required = false)
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
}
class LoginRequest {
private String username;
private String password;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
package com.jh.common.security;
/**
* The Class PasswordHash.
*/
public class PasswordHash {
/** The hash. */
private String hash;
/** The salt. */
private String salt;
/** The iterations. */
private int iterations;
/**
* Instantiates a new password hash.
*
* @param hash the hash
* @param salt the salt
*/
public PasswordHash(String hash, String salt) {
this(hash, salt, 1);
}
/**
* Instantiates a new password hash.
*
* @param hash the hash
* @param salt the salt
* @param iterations the iterations
*/
public PasswordHash(String hash, String salt, int iterations) {
super();
this.hash = hash;
this.salt = salt;
this.iterations = iterations;
}
/**
* Gets the hash.
*
* @return the hash
*/
public String getHash() {
return hash;
}
/**
* Gets the salt.
*
* @return the salt
*/
public String getSalt() {
return salt;
}
/**
* Gets the iterations.
*
* @return the iterations
*/
public int getIterations() {
return iterations;
}
/**
* @return
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "PasswordHash [hash=" + hash + ", salt=" + salt + ", iterations=" + iterations + "]";
}
}
package com.jh.common.security;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import org.springframework.util.StringUtils;
/**
* The Class PasswordSecurity.
*/
public abstract class PasswordSecurity {
/** The Constant ITERATIONS. */
private static final int ITERATIONS = 1;
/** The b 64 encoder. */
private static Encoder b64Encoder = Base64.getEncoder();
/** The b 64 decoder. */
private static Decoder b64Decoder = Base64.getDecoder();
/**
* Hash password.
*
* @param password the password
* @return the password hash
*/
public static PasswordHash hashPassword(String password) {
try {
byte[] salt = getSalt();
String hash = createHash(password, salt);
String salt64 = new String(b64Encoder.encode(salt));
return new PasswordHash(hash, salt64);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Check password.
*
* @param password the password
* @param storedHash the stored hash
* @param storedSalt the stored salt
* @return true, if successful
*/
public static boolean checkPassword(String password, String storedHash, String storedSalt) {
if (password == null || storedSalt == null || storedHash == null) {
return false;
}
byte[] salt = b64Decoder.decode(storedSalt);
try {
String hash = createHash(password, salt);
return hash.equals(storedHash);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Validate password.
*
* @param password the password
* @return true, if successful
*/
public static boolean validatePassword(String password) {
return StringUtils.hasText(password);
}
/**
* Creates the hash.
*
* @param password the password
* @param salt the salt
* @return the string
* @throws NoSuchAlgorithmException the no such algorithm exception
* @throws InvalidKeySpecException the invalid key spec exception
*/
protected static String createHash(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, 2048);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
String hash = new String(b64Encoder.encode(factory.generateSecret(spec).getEncoded()));
spec.clearPassword();
return hash;
}
/**
* Gets the salt.
*
* @return the salt
* @throws NoSuchAlgorithmException the no such algorithm exception
*/
private static byte[] getSalt() throws NoSuchAlgorithmException {
//Always use a SecureRandom generator
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
//Create array for salt
byte[] salt = new byte[128];
//Get a random salt
sr.nextBytes(salt);
//return salt
return salt;
}
}
package com.jh.common.security;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* The Class ReactApplicationSecurityContext.
*/
public class ReactApplicationSecurityContext extends WebSecurityConfigurerAdapter {
/**
* @param http
* @throws Exception
* @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/auth/signup").permitAll()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll()
.and()
.addFilterBefore(authFilter(), UsernamePasswordAuthenticationFilter.class)
.logout()
.clearAuthentication(true)
.permitAll();
http.csrf().disable();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint());
http.headers().frameOptions().sameOrigin();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
}
/**
* @param auth
* @throws Exception
* @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder)
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.eraseCredentials(true);
}
/**
* Auth filter.
*
* @return the json authentication filter
* @throws Exception the exception
*/
protected JsonAuthenticationFilter authFilter() throws Exception {
JsonAuthenticationFilter filter = new JsonAuthenticationFilter();
filter.setFilterProcessesUrl("/api/auth/login");
filter.setAuthenticationSuccessHandler(new RestAuthenticationSuccessHandler());
filter.setAuthenticationManager(authenticationManagerBean());
filter.setUsernameParameter("email");
return filter;
}
/**
* Override this method to provide custom {@link AuthenticationEntryPoint}.
*
* It returns {@link RestAuthenticationEntryPoint} by default
*
* @return the authentication entry point
*/
protected AuthenticationEntryPoint authenticationEntryPoint() {
return new RestAuthenticationEntryPoint();
}
/* *** LOGIN & SIGNUP configuration *** */
/**
* Auth client resources.
*
* @return the web mvc configurer adapter
*/
@Bean
public WebMvcConfigurerAdapter authClientResources() {
return new WebMvcConfigurerAdapter() {
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/auth/**").addResourceLocations("classpath:/com/jh/common/security/controller/");
}
// @Override
// public void addViewControllers(ViewControllerRegistry registry) {
// registry.addViewController("/auth/login").setViewName("forward:/auth/loginForm.html");
// registry.addViewController("/auth/signup").setViewName("forward:/auth/signupForm.html");
// }
};
}
}
package com.jh.common.security;
/**
* The Class ResetPassword.
*/
public class ResetPassword {
/** The username. */
private String username;
/** The password. */
private String password;
private String token;
/**
* Gets the username.
*
* @return the username
*/
public String getUsername() {
return username;
}
/**
* Sets the username.
*
* @param username the new username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Gets the password.
*
* @return the password
*/
public String getPassword() {
return password;
}
/**
* Sets the password.
*
* @param password the new password
*/
public void setPassword(String password) {
this.password = password;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
package com.jh.common.security;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
/**
* The Class RestAuthenticationEntryPoint.
*/
public final class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
/**
* @param request
* @param response
* @param authException
* @throws IOException
* @see org.springframework.security.web.AuthenticationEntryPoint#commence(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.AuthenticationException)
*/
@Override
public final void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
\ No newline at end of file
package com.jh.common.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
/**
* The Class RestAuthenticationSuccessHandler.
*/
public class RestAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{
/**
* Instantiates a new rest authentication success handler.
*/
public RestAuthenticationSuccessHandler(){
super();
}
/**
* @param request
* @param response
* @param authentication
* @throws ServletException
* @throws IOException
* @see org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler#onAuthenticationSuccess(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.Authentication)
*/
@Override
public void onAuthenticationSuccess( final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication ) throws ServletException, IOException{
//super.onAuthenticationSuccess( request, response, authentication );
this.clearAuthenticationAttributes( request );
}
}
\ No newline at end of file
package com.jh.common.security;
import java.util.Locale;
/**
* The Class Signup.
*/
public class Signup {
/** The username. */
private String username;
/** The password. */
private String password;
/** The locale. */
private Locale locale = Locale.ENGLISH;
/**
* Gets the username.
*
* @return the username
*/
public String getUsername() {
return username;
}
/**
* Sets the username.
*
* @param username the new username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Gets the password.
*
* @return the password
*/
public String getPassword() {
return password;
}
/**
* Sets the password.
*
* @param password the new password
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Gets the locale.
*
* @return the locale
*/
public Locale getLocale() {
return locale;
}
/**
* Sets the locale.
*
* @param locale the new locale
*/
public void setLocale(Locale locale) {
this.locale = locale;
}
}
package com.jh.common.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
/**
* The Class UserAuthenticationProvider.
*/
//TODO
public class UserAuthenticationProvider implements AuthenticationProvider {
/** The auth service. */
@Autowired
private AuthService authService;
/**
* Authenticate.
*
* @param authentication the authentication
* @return the authentication
* @throws AuthenticationException the authentication exception
* @see org.springframework.security.authentication.AuthenticationProvider#authenticate(org.springframework.security.core.Authentication)
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
String email = String.valueOf(token.getPrincipal());
String password = String.valueOf(token.getCredentials());
//TODO refactor
return authService.authenticate(email, password);
}
/**
* Supports.
*
* @param authentication the authentication
* @return true, if successful
* @see org.springframework.security.authentication.AuthenticationProvider#supports(java.lang.Class)
*/
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
/*
* Created on 28. 7. 2017 9:44:08
*
*/
package com.jh.common.security.controller;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.ApplicationListener;
import org.springframework.context.MessageSource;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.jh.common.security.AuthService;
import com.jh.common.security.PasswordSecurity;
import com.jh.common.security.ResetPassword;
import com.jh.common.security.Signup;
import com.jh.common.template.TemplateService;
import com.jh.common.web.error.NotFoundException;
/**
* The Class AuthController.
*
* @author Jan Hrabal
*/
@Controller
@ConditionalOnExpression("${auth.controller.enabled:true}")
public class AuthController implements ApplicationListener<ContextRefreshedEvent> {
/** The style href. */
@Value("${auth.html.style.href:/p.css}")
private String styleHref;
/** The content encoding. */
@Value("${public.content.encoding:UTF-8}")
private String contentEncoding;
/** The template service. */
@Autowired
private TemplateService templateService;
/** The auth service. */
@Autowired
private AuthService authService;
/** The message source. */
private MessageSource messageSource;
@Value("${auth.controller.signup.enabled:true}")
private boolean signupEnabled;
@Value("${auth.controller.reset.enabled:true}")
private boolean resetEnabled;
@Value("${auth.controller.home.enabled:true}")
private boolean homeEnabled;
private static boolean signupEnabledStatic = true;
private static boolean resetEnabledStatic = true;
private static boolean homeEnabledStatic = true;
/**
* Inits the.
*/
@PostConstruct
public void init() {
if (contentEncoding != null) {
contentEncoding = contentEncoding.toLowerCase();
} else {
contentEncoding = "utf-8";
}
}
/**
* Logout.
*
* @param session the session
* @return the string
*/
@RequestMapping({"logout", "/auth/logout"})
public String logout(HttpSession session) {
doLogout(session);
return "redirect:/";
}
/**
* Do logout.
*
* @param session the session
*/
protected void doLogout(HttpSession session) {
SecurityContextHolder.clearContext();
if (session != null) {
session.invalidate();
}
}
/**
* Logout api.
*
* @param session the session
*/
@RequestMapping({"/api/auth/logout"})
@ResponseStatus(HttpStatus.NO_CONTENT)
public void logoutApi(HttpSession session) {
doLogout(session);
}
/**
* Login form.
*
* @param locale the locale
* @return the response entity
*/
@RequestMapping("/auth/login")
public ResponseEntity<String> loginForm(Locale locale, @RequestParam(name = "t", required = false) String t) {
if (StringUtils.hasText(t) && !t.startsWith("/")) {
t = null;
}
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", String.format("text/html; charset=%s", contentEncoding));
StringWriter sw = new StringWriter();
templateService.evaluateTemplate("loginForm.html", getClass(), authHtmlParams(locale, messageSource, styleHref, t), locale, sw);
return new ResponseEntity<>(sw.toString(), headers, HttpStatus.OK);
}
/**
* Signup form.
*
* @param locale the locale
* @return the response entity
*/
@RequestMapping("/auth/signup")
public ResponseEntity<String> signupForm(Locale locale) {
if (!signupEnabled) {
throw new NotFoundException();
}
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", String.format("text/html; charset=%s", contentEncoding));
StringWriter sw = new StringWriter();
templateService.evaluateTemplate("signupForm.html", getClass(), authHtmlParams(locale, messageSource, styleHref, null), locale, sw);
return new ResponseEntity<>(sw.toString(), headers, HttpStatus.OK);
}
/**
* Signup.
*
* @param signup the signup
* @param locale the locale
* @return the response entity
*/
@RequestMapping(path = "/api/auth/signup", method = RequestMethod.POST)
public ResponseEntity<List<AuthError>> signup(@RequestBody Signup signup, Locale locale) {
if (!signupEnabled) {
throw new NotFoundException();
}
//validate
if (signup == null) {
return new ResponseEntity<>(Collections.singletonList(new AuthError(null, "BadRequest")), HttpStatus.BAD_REQUEST);
}
signup.setLocale(locale);
List<AuthError> errors = new ArrayList<>();
//TODO validate email
if (!StringUtils.hasText(signup.getUsername())) {
errors.add(new AuthError("username", "BadUsername"));
}
if (!PasswordSecurity.validatePassword(signup.getPassword())) {
errors.add(new AuthError("password", "BadPassword"));
}
try {
List<AuthError> errs = authService.register(signup);
if (errs == null || errs.isEmpty()) {
Authentication auth = authService.authenticate(signup.getUsername(), signup.getPassword());
SecurityContextHolder.getContext().setAuthentication(auth);
} else {
errors.addAll(errs);
}
} catch (Exception e) {
e.printStackTrace();
errors.add(new AuthError(null, e.getMessage()));
}
//everything was OK - log user in
if (errors.isEmpty()) {
//return no errors
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
@RequestMapping(path = "/api/auth/resetRequest", method = RequestMethod.POST)
public ResponseEntity<List<AuthError>> resetToken(@RequestBody ResetPassword request) {
if (!resetEnabled) {
throw new NotFoundException();
}
authService.generateResetToken(request.getUsername());
return ResponseEntity.accepted().build();
}
@RequestMapping(path = "/api/auth/resetPassword", method = RequestMethod.POST)
public ResponseEntity<List<AuthError>> reset(@RequestBody ResetPassword resetPassword) {
if (!resetEnabled) {
throw new NotFoundException();
}
String token = resetPassword.getToken();
if (!StringUtils.hasText(token)) {
return new ResponseEntity<>(Collections.singletonList(new AuthError(null, "BadRequest")), HttpStatus.BAD_REQUEST);
}
List<AuthError> errors = new ArrayList<>();
try {
List<AuthError> errs = authService.resetPassword(resetPassword);
if (errs == null || errs.isEmpty()) {
Authentication auth = authService.authenticate(resetPassword.getUsername(), resetPassword.getPassword());
SecurityContextHolder.getContext().setAuthentication(auth);
} else {
errors.addAll(errs);
}
} catch (Exception e) {
e.printStackTrace();
errors.add(new AuthError(null, e.getMessage()));
}
if (errors.isEmpty()) {
//return no errors
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
@RequestMapping("/auth/reset")
public ResponseEntity<String> resetForm(Locale locale) {
if (!resetEnabled) {
throw new NotFoundException();
}
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", String.format("text/html; charset=%s", contentEncoding));
StringWriter sw = new StringWriter();
templateService.evaluateTemplate("resetForm.html", getClass(), authHtmlParams(locale, messageSource, styleHref, null), locale, sw);
return new ResponseEntity<>(sw.toString(), headers, HttpStatus.OK);
}
/** The Constant ERRORS. */
//
static final String[] ERRORS = {
"UsernameRequired",
"PasswordRequired",
"UnexpectedError",
"UsernameTaken",
"BadUsername",
"BadPassword",
"BadUsernameOrPassword",
"ResetTokenSent"
};
/** The Constant ERROR_MSGS. */
static final String[] ERROR_MSGS = Arrays.stream(ERRORS, 0, ERRORS.length).map(s -> { char c[] = s.toCharArray(); c[0] += 32; return String.format("auth.error.%s", new String(c)); }).collect(Collectors.toList()).toArray(new String[0]);
/**
* Auth html params.
*
* @param locale the locale
* @param messageSource the message source
* @param styleHref the style href
* @return the map
*/
public static Map<String, Object> authHtmlParams(Locale locale, MessageSource messageSource, String styleHref, String originalTarget) {
Map<String, Object> params = new HashMap<>();
params.put("_styleHref", styleHref);
if (StringUtils.hasText(originalTarget)) {
params.put("loginTargetUri", originalTarget);
} else {
params.put("loginTargetUri", "/");
}
StringBuilder sb = new StringBuilder("\t\t\twindow.__labels = [];\n");
for (int i = 0; i < ERROR_MSGS.length; i++) {
String s = ERROR_MSGS[i];
String msg = messageSource.getMessage(s, null, s, locale);
if (msg != null) {
msg = msg.replaceAll("\\n", "\\\\n").replaceAll("\"", "\\\"");
sb.append("\t\t\twindow.__labels['").append(ERRORS[i]).append("'] = \"").append(msg.trim()).append("\";\n");
}
}
params.put("_labels", sb);
//new params
params.put("signupEnabled", signupEnabledStatic);
params.put("resetEnabled", resetEnabledStatic);
params.put("homeEnabled", homeEnabledStatic);
return params;
}
/**
* Sets the message source.
*
* @param messageSource the new message source
*/
@Autowired(required = false)
@Qualifier("i18nMessageSource")
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
signupEnabledStatic = signupEnabled;
resetEnabledStatic = resetEnabled;
homeEnabledStatic = homeEnabled;
}
}
package com.jh.common.security.controller;
/**
* The Class AuthError.
*/
public class AuthError {
/** The Constant USERNAME_FIELD. */
public static final String USERNAME_FIELD = "username";
/** The Constant PASSWORD_FIELD. */
public static final String PASSWORD_FIELD = "password";
/** The field. */
String field;
/** The code. */
String code;
/**
* Instantiates a new auth error.
*
* @param field the field
* @param error the error
*/
public AuthError(String field, String error) {
super();
this.field = field;
this.code = error;
}
/**
* Gets the field.
*
* @return the field
*/
public String getField() {
return field;
}
/**
* Gets the code.
*
* @return the code
*/
public String getCode() {
return code;
}
/**
* Username error.
*
* @param error the error
* @return the auth error
*/
public static AuthError usernameError(String error) {
return new AuthError(USERNAME_FIELD, error);
}
/**
* Password error.
*
* @param error the error
* @return the auth error
*/
public static AuthError passwordError(String error) {
return new AuthError(PASSWORD_FIELD, error);
}
}
LoginTitle=Přihlášení
LoginUsername=Přihlašovací jméno
LoginPassword=Heslo
LoginButton=Přihlásit
LoginButtonBack=Zpět
LoginResetPasswordMessage=Zapomenuté heslo?
LoginButtonResetPassword=Obnovit heslo
LoginSignupMessage=Nemáte ještě svůj účet?
LoginButtonSignup=Vytvořit nový účet
LoginErrorEmailRequired=Přihlašovací jméno musí být vyplněno
LoginErrorPasswordRequired=Heslo musí být vyplněno
LoginErrorBadCredentials=Neplatné přihlašovací údaje
LoginErrorUnexpected=Nastala neočekávaná chyba
window.login = {
login: function(user, opts) {
jh.post('/api/auth/login', user, function(status, data) {
if (status > 199 && status < 300) {
var user = data;
if (typeof opts.onSuccess == "function") {
opts.onSuccess(user);
}
} else if (status == 401 || status == 403) {
opts.onError([{ field: "username", code: 'BadUsernameOrPassword' }]);
} else {
if (typeof opts.onError == "function") {
var err = data;
if (err) {
if (!(err instanceof Array)) {
err = [err];
}
var errs = [];
var i = null;
for (i = 0; i < err.length; i++) {
errs.push(err[i] || { code: 'UnexpectedError' });
}
opts.onError(errs);
}
}
}
});
},
reset: function(login, opts) {
jh.post('/api/auth/resetRequest', { username: login }, function(status, data) {
if (status > 199 && status < 300) {
var user = data;
if (typeof opts.onSuccess == "function") {
opts.onSuccess(user);
}
} else {
if (typeof opts.onError == "function") {
var err = data;
if (err) {
if (!(err instanceof Array)) {
err = [err];
}
var errs = [];
var i = null;
for (i = 0; i < err.length; i++) {
errs.push(err[i] || { code: 'UnexpectedError' });
}
opts.onError(errs);
}
}
}
});
},
form: function(opts) {
if (!opts) {
opts = {};
}
var
that = this,
formId = opts.formId || "loginForm",
usernameId = opts.usernameId || "loginUsername",
passwordId = opts.passwordId || "loginPassword",
loginErrorId = opts.loginErrorId || "loginErrorId",
resetLinkId = opts.resetLinkId || "resetLink",
loginButtonId = opts.loginButtonId || "loginButton",
activityIndicatorId = opts.activityIndicatorId || "authActivityIndicator",
onSubmitHandler = typeof opts.onSubmitHandler == "function" ? opts.onSubmitHandler : function() {},
onSuccessHandler = typeof opts.onSuccessHandler == "function" ? opts.onSuccessHandler : function() {
window.location = opts.loginTargetUri || "/";
},
onErrorHandler = typeof opts.onErrorHandler == "function" ? opts.onErrorHandler : function(errs) {
if (!errs || !errs.length) {
return;
}
var
e = null,
i = null,
el = null,
field = null,
errEl = null;
for (i = 0; i < errs.length; i++) {
e = errs[i];
if (e.field) {
if (e.field == "username") {
field = usernameId;
} else if (e.field == "password") {
field = passwordId;
}
}
if (field) {
el = jh.dom(field);
jh.addClass(el, "error");
errEl = jh.dom(field + 'Error');
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
} else {
errEl = jh.dom(loginErrorId);
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
}
}
},
clearErrors = typeof opts.clearErrors == "function" ? opts.clearErrors : function() {
clearError(usernameId + "Error");
clearError(passwordId + "Error");
clearError(loginErrorId);
},
clearError = typeof opts.clearError == "function" ? opts.clearError : function (id) {
var el = jh.dom(id);
el.innerHTML = "";
jh.hide(el);
},
disable = function() {
jh.disable(loginButtonId);
jh.disable(usernameId);
jh.disable(passwordId);
jh.show(activityIndicatorId);
},
enable = function() {
jh.enable(loginButtonId);
jh.enable(usernameId);
jh.enable(passwordId);
jh.hide(activityIndicatorId);
},
form = jh.dom(formId),
username = jh.dom(usernameId),
password = jh.dom(passwordId),
translate = typeof opts.translate === "function" ? opts.translate : function(key) {
if (window.__labels) {
return window.__labels[key] || key;
}
return key;
};
var resetLink = jh.dom(resetLinkId);
resetLink.onclick = function(e) {
if (event) {
event.returnValue = false;
}
if (e) {
e.preventDefault();
}
if (!username.value) {
onErrorHandler([{
error: translate('UsernameRequired'),
field: "username"
}]);
return false;
}
that.reset(username.value, {
onSuccess: function() {
onErrorHandler([{
error: translate('ResetTokenSent')
}]);
},
onError: onErrorHandler
});
return false;
};
form.onsubmit = function() {
var
user = {};
user.username = username.value;
user.password = password.value;
disable();
clearErrors();
onSubmitHandler(user);
var errors = [];
if (!user.username) {
errors.push({
error: translate('UsernameRequired'),
field: "username"
});
}
if (!user.password) {
errors.push({
error: translate('PasswordRequired'),
field: "password"
});
}
if (errors.length) {
onErrorHandler(errors);
enable();
return false;
}
//clear errors
that.login(user, {
onSuccess: function(user) {
onSuccessHandler(user);
},
onError: function(errors) {
var
errs = [];
for (var i = 0; i < errors.length; i++) {
err = errors[i] || {};
var label = translate(err.code || 'UnexpectedError');
errs.push({
field: err.field,
label: label
});
}
onErrorHandler(errs);
enable();
}
});
return false;
};
}
};
<!DOCTYPE html>
<html lang="en" class="auth-body">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="cache-control" content="max-age=0">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT">
<meta http-equiv="pragma" content="no-cache">
<link rel="stylesheet" type="text/css" th:href="${_styleHref}" href="p.css"/>
<title th:text="#{login.title}">Login</title>
<!-- [[#{login.error.usernameRequired}]] -->
<script type="text/javascript" th:utext="${_labels}">
/* TODO */
window.__labels = [];
window.__labels['UsernameRequired'] = "Vyplňte prosím e-mail";
window.__labels['PasswordRequired'] = "Vyplňte prosím heslo";
window.__labels['UnexpectedError'] = "Došlo k neočekávané chybě";
window.__labels['UsernameTaken'] = "Uživatel již existuje. Zvolte prosím jiný e-mail";
window.__labels['BadUsername'] = "Neplatný e-mail";
window.__labels['BadPassword'] = "Neplatné heslo";
window.__labels['BadUsernameOrPassword'] = "Neplatný e-mail nebo heslo";
window.__labels['ResetTokenSent'] = "Na emailovou adresu byl zaslán postup pro obnovení hesla";
</script>
</head>
<body class="auth-body">
<div class="auth-page">
<div class="auth-form">
<h1 class="small" th:text="#{login.title}">Login</h1>
<div id="loginError" class="auth-form-field-error" style="display:none;"></div>
<form id="loginForm">
<div id="loginErrorId"></div>
<div class="auth-form-input">
<input id="loginUsername" type="text" placeholder="username (email)" th:placeholder="#{login.login}"/>
<div id="loginUsernameError" class="auth-form-field-error"></div>
</div>
<div class="auth-form-input">
<input id="loginPassword" type="password" placeholder="password" th:placeholder="#{login.password}"/>
<div id="loginPasswordError" class="auth-form-field-error"></div>
</div>
<div class="auth-button-container">
<button id="loginButton" th:text="#{login.loginAction}">Login</button>
<div id="authActivityIndicator" class="activity-indicator-container" style="display:none;">
<div class="ball-pulse">
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
<div>
<p class="message" th:if="${signupEnabled}">
<a href="/auth/signup" th:text="#{login.signupLink}">Create new account</a>
</p>
<p class="message" th:if="${homeEnabled}">
<a href="/" th:text="#{login.homeLink}">Home</a>
</p>
</div>
<div>
<p class="message" th:if="${resetEnabled}">
<a href="/auth/reset" id="resetLink" th:text="#{login.resetLink}">Having trouble logging in?</a>
</p>
</div>
</form>
</div>
</div>
<script type="text/javascript" src="/utils.js"></script>
<script type="text/javascript" src="/auth/login.js"></script>
<script defer="defer">
(function() {
login.form({
formId: "loginForm",
usernameId: "loginUsername",
passwordId: "loginPassword",
loginTargetUri: "[[${loginTargetUri}]]",
resetTargetUri: "[[${resetTargetUri}]]"
});
})();
</script>
</body>
</html>
\ No newline at end of file
#LOGIN page
login.title=Login
login.login=Email
login.loginPlaceholder=your@email
login.password=Password
login.passwordPlaceholder=your secure password
login.loginAction=Login
login.signupLink=Create new account
login.homeLink=Home
login.resetLink=Having trouble logging in?
#SIGNUP page
signup.title=Signup
signup.username=Email
signup.usernamePlaceholder=your@email
signup.password=Password
signup.passwordPlaceholder=your secure password
signup.signupAction=Create my free account
signup.disclaimer=By clicking "Create my free account", you agree to our <a href="/terms.html" target="_terms"> terms of service</a>
signup.loginLink=Login
#RESET page
reset.title=Reset password
reset.username=Email
reset.usernamePlaceholder=your@email
reset.password=New password
reset.passwordPlaceholder=your new password
reset.resetAction=Change password
reset.loginLink=Login
#RESET email
email.reset.subject=Password reset request
email.reset.title=Password reset request
email.reset.text=A new request for a password change has been issued.\n\nTo reset your password, go to <a href="{0}">{0}</a>\n\nThe link is active for 3 days
email.reset.html=A new request for a password change has been issued.<br/><br/>To reset your password, go to <a href="{0}">{0}</a><br/><br/><i><sup>*</sup>The link is active for 3 days</i>
#AUTH errors
auth.error.usernameRequired=Email is required
auth.error.passwordRequired=Password is required
auth.error.unexpectedError=Unexpected error occured
auth.error.usernameTaken=Email is already registered
auth.error.badUsername=Bad email
auth.error.badPassword=Bad password
auth.error.badUsernameOrPassword=Bad email or password
auth.error.resetTokenSent=An email with instructions has been sent to your email address
auth.error.resetInvalidToken=Invalid token
#LOGIN page
login.title=Přihlášení
login.login=Email
login.loginPlaceholder=váš@email
login.password=Heslo
login.passwordPlaceholder=vaše bezpečné heslo
login.loginAction=Přihlásit
login.signupLink=Založit nový účet
login.homeLink=Domů
login.resetLink=Máte problémy s přihlášením?
#SIGNUP page
signup.title=Registrace
signup.username=Email
signup.usernamePlaceholder=váš@email
signup.password=Heslo
signup.passwordPlaceholder=vaše bezpečné heslo
signup.signupAction=Založit účet zdarma
signup.disclaimer=Kliknutím na "Založit účet zdarma" souhlasíte s&#160;našimi <a href="/terms.html" target="_terms"> podmínkami služby</a>
signup.loginLink=Přihlášení
#RESET page
reset.title=Obnova hesla
reset.username=Email
reset.usernamePlaceholder=váš@email
reset.password=Nové heslo
reset.passwordPlaceholder=vaše nové heslo
reset.resetAction=Nastavit
reset.loginLink=Login
#RESET email
email.reset.subject=Požadavek na obnovení hesla
email.reset.title=Požadavek na obnovení hesla
email.reset.text=Přijali jsme nový požadavek na změnu Vašeho hesla.\n\nPro obnovení Vašeho hesla prosím navštivte {0}\n\nOdkaz je funkční po dobu 3 dnů
email.reset.html=Přijali jsme nový požadavek na změnu Vašeho hesla.<br/><br/>Pro obnovení Vašeho hesla prosím navštivte <a href="{0}">{0}</a><br/><br/><i><sup>*</sup>Odkaz je funkční po dobu 3 dnů</i>
#AUTH errors
auth.error.usernameRequired=Vyplňte prosím e-mail
auth.error.passwordRequired=Vyplňte prosím heslo
auth.error.unexpectedError=Došlo k neočekávané chybě
auth.error.usernameTaken=Uživatel již existuje. Zvolte prosím jiný e-mail
auth.error.badUsername=Neplatný e-mail
auth.error.badPassword=Neplatné heslo
auth.error.badUsernameOrPassword=Neplatný e-mail nebo heslo
auth.error.resetTokenSent=E-mail s instrukcemi byl zaslán na vaši e-mailovou adresu
window.reset = {
getParameter: function(parameterName) {
if (!location.search || !location.search.length) {
return null
}
var result = null,
tmp = [];
var items = location.search.substr(1).split("&");
for (var index = 0; index < items.length; index++) {
tmp = items[index].split("=");
if (tmp[0] === parameterName) {
result = decodeURIComponent(tmp[1]);
}
}
return result;
},
reset: function(user, opts) {
jh.post('/api/auth/resetPassword', user, function(status, data) {
if (status > 199 && status < 300) {
var user = data;
if (typeof opts.onSuccess == "function") {
opts.onSuccess(user);
}
} else {
if (typeof opts.onError == "function") {
var err = data;
if (err) {
if (!(err instanceof Array)) {
err = [err];
}
var errs = [];
var i = null;
for (i = 0; i < err.length; i++) {
errs.push(err[i] || { code: 'UnexpectedError' });
}
opts.onError(errs);
}
}
}
});
},
form: function(opts) {
if (!opts) {
opts = {};
}
var
that = this,
formId = opts.formId || "resetForm",
usernameId = opts.usernameId || "resetUsername",
passwordId = opts.passwordId || "resetPassword",
resetErrorId = opts.resetErrorId || "resetErrorId",
loginButtonId = opts.resetButtonId || "resetButton",
activityIndicatorId = opts.activityIndicatorId || "authActivityIndicator",
onSubmitHandler = typeof opts.onSubmitHandler == "function" ? opts.onSubmitHandler : function() {},
onSuccessHandler = typeof opts.onSuccessHandler == "function" ? opts.onSuccessHandler : function() {
window.location= '/';
},
onErrorHandler = typeof opts.onErrorHandler == "function" ? opts.onErrorHandler : function(errs) {
if (!errs || !errs.length) {
return;
}
var
e = null,
i = null,
el = null,
field = null,
errEl = null;
for (i = 0; i < errs.length; i++) {
e = errs[i];
if (e.field) {
if (e.field == "username") {
field = usernameId;
} else if (e.field == "password") {
field = passwordId;
}
}
if (field) {
el = jh.dom(field);
jh.addClass(el, "error");
errEl = jh.dom(field + 'Error');
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
} else {
errEl = jh.dom(resetErrorId);
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
}
}
},
disable = function() {
jh.disable(loginButtonId);
jh.disable(usernameId);
jh.disable(passwordId);
jh.show(activityIndicatorId);
},
enable = function() {
jh.enable(loginButtonId);
jh.enable(usernameId);
jh.enable(passwordId);
jh.hide(activityIndicatorId);
},
clearErrors = typeof opts.clearErrors == "function" ? opts.clearErrors : function() {
clearError(usernameId + "Error");
clearError(passwordId + "Error");
clearError(resetErrorId);
},
clearError = typeof opts.clearError == "function" ? opts.clearError : function (id) {
var el = jh.dom(id);
el.innerHTML = "";
jh.hide(el);
},
form = jh.dom(formId),
username = jh.dom(usernameId),
password = jh.dom(passwordId),
translate = typeof opts.translate === "function" ? opts.translate : function(key) {
if (window.__labels) {
return window.__labels[key] || key;
}
return key;
};
form.onsubmit = function() {
var
user = {};
user.username = username.value;
user.password = password.value;
user.token = that.getParameter("t");
disable();
clearErrors();
onSubmitHandler(user);
var errors = [];
if (!user.username) {
errors.push({
code: 'UsernameRequired',
label: translate('UsernameRequired'),
field: "username"
});
}
if (!user.password) {
errors.push({
code: 'PasswordRequired',
label: translate('PasswordRequired'),
field: "password"
});
}
if (errors.length) {
onErrorHandler(errors);
enable();
return false;
}
//clear errors
that.reset(user, {
onSuccess: function(user) {
onSuccessHandler(user);
},
onError: function(errors) {
var
errs = [];
for (var i = 0; i < errors.length; i++) {
err = errors[i] || {};
var label = translate(err.code || 'UnexpectedError');
errs.push({
field: err.field,
label: label,
code: err.code
});
}
onErrorHandler(errs);
enable();
}
});
return false;
};
}
};
<!DOCTYPE html>
<html lang="en" class="auth-body">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="cache-control" content="max-age=0">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT">
<meta http-equiv="pragma" content="no-cache">
<link rel="stylesheet" type="text/css" th:href="${_styleHref}" href="p.css"/>
<title th:text="#{reset.title}">reset</title>
<script type="text/javascript" th:utext="${_labels}">
/* TODO */
window.__labels = [];
window.__labels['UsernameRequired'] = "Vyplňte prosím e-mail";
window.__labels['PasswordRequired'] = "Vyplňte prosím heslo";
window.__labels['UnexpectedError'] = "Došlo k neočekávané chybě";
window.__labels['BadUsername'] = "Neplatný e-mail";
window.__labels['BadPassword'] = "Neplatné heslo";
window.__labels['BadUsernameOrPassword'] = "Neplatný e-mail nebo heslo";
</script>
</head>
<body class="auth-body">
<div class="auth-page">
<div class="auth-form">
<h1 class="small" th:text="#{reset.title}">reset</h1>
<div id="resetError" class="auth-form-field-error" style="display:none;"></div>
<form id="resetForm">
<div id="resetErrorId"></div>
<div class="auth-form-input">
<input id="resetUsername" type="text" placeholder="username (email)" th:placeholder="#{reset.usernamePlaceholder}"/>
<div id="resetUsernameError" class="auth-form-field-error"></div>
</div>
<div class="auth-form-input">
<input id="resetPassword" type="password" placeholder="password" th:placeholder="#{reset.passwordPlaceholder}"/>
<div id="resetPasswordError" class="auth-form-field-error"></div>
</div>
<div class="auth-button-container">
<button id="resetButton" th:text="#{reset.resetAction}">Reset password</button>
<div id="authActivityIndicator" class="activity-indicator-container" style="display:none;">
<div class="ball-pulse">
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
<div>
<p class="message">
<a href="/auth/login" th:text="#{reset.loginLink}">Login</a>
</p>
</div>
</form>
</div>
</div>
<script type="text/javascript" src="/utils.js"></script>
<script type="text/javascript" src="/auth/reset.js"></script>
<script defer="defer">
(function() {
reset.form({
formId: "resetForm",
usernameId: "resetUsername",
passwordId: "resetPassword"
});
})();
</script>
</body>
</html>
\ No newline at end of file
window.signup = {
signup: function(user, opts) {
jh.post('/api/auth/signup', user, function(status, data) {
if (status > 199 && status < 300) {
var user = data;
if (typeof opts.onSuccess == "function") {
opts.onSuccess(user);
}
} else {
if (typeof opts.onError == "function") {
var err = data;
if (err) {
if (!(err instanceof Array)) {
err = [err];
}
var errs = [];
var i = null;
for (i = 0; i < err.length; i++) {
errs.push(err[i] || { code: 'UnexpectedError' });
}
opts.onError(errs);
}
}
}
});
},
form: function(opts) {
if (!opts) {
opts = {};
}
var
that = this,
formId = opts.formId || "signupForm",
usernameId = opts.usernameId || "signupUsername",
passwordId = opts.passwordId || "signupPassword",
signupErrorId = opts.signupErrorId || "signupErrorId",
loginButtonId = opts.signupButtonId || "signupButton",
activityIndicatorId = opts.activityIndicatorId || "authActivityIndicator",
onSubmitHandler = typeof opts.onSubmitHandler == "function" ? opts.onSubmitHandler : function() {},
onSuccessHandler = typeof opts.onSuccessHandler == "function" ? opts.onSuccessHandler : function() {
window.location= '/';
},
onErrorHandler = typeof opts.onErrorHandler == "function" ? opts.onErrorHandler : function(errs) {
if (!errs || !errs.length) {
return;
}
var
e = null,
i = null,
el = null,
field = null,
errEl = null;
for (i = 0; i < errs.length; i++) {
e = errs[i];
if (e.field) {
if (e.field == "username") {
field = usernameId;
} else if (e.field == "password") {
field = passwordId;
}
}
if (field) {
el = jh.dom(field);
jh.addClass(el, "error");
errEl = jh.dom(field + 'Error');
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
} else {
errEl = jh.dom(signupErrorId);
errEl.innerHTML = e.label || e.error;
jh.show(errEl);
}
}
},
disable = function() {
jh.disable(loginButtonId);
jh.disable(usernameId);
jh.disable(passwordId);
jh.show(activityIndicatorId);
},
enable = function() {
jh.enable(loginButtonId);
jh.enable(usernameId);
jh.enable(passwordId);
jh.hide(activityIndicatorId);
},
clearErrors = typeof opts.clearErrors == "function" ? opts.clearErrors : function() {
clearError(usernameId + "Error");
clearError(passwordId + "Error");
clearError(signupErrorId);
},
clearError = typeof opts.clearError == "function" ? opts.clearError : function (id) {
var el = jh.dom(id);
el.innerHTML = "";
jh.hide(el);
},
form = jh.dom(formId),
username = jh.dom(usernameId),
password = jh.dom(passwordId),
translate = typeof opts.translate === "function" ? opts.translate : function(key) {
if (window.__labels) {
return window.__labels[key] || key;
}
return key;
};
form.onsubmit = function() {
var
user = {};
user.username = username.value;
user.password = password.value;
disable();
clearErrors();
onSubmitHandler(user);
var errors = [];
if (!user.username) {
errors.push({
code: 'UsernameRequired',
label: translate('UsernameRequired'),
field: "username"
});
}
if (!user.password) {
errors.push({
code: 'PasswordRequired',
label: translate('PasswordRequired'),
field: "password"
});
}
if (errors.length) {
onErrorHandler(errors);
enable();
return false;
}
//clear errors
that.signup(user, {
onSuccess: function(user) {
onSuccessHandler(user);
},
onError: function(errors) {
var
errs = [];
for (var i = 0; i < errors.length; i++) {
err = errors[i] || {};
var label = translate(err.code || 'UnexpectedError');
errs.push({
field: err.field,
label: label,
code: err.code
});
}
onErrorHandler(errs);
enable();
}
});
return false;
};
}
};
<!DOCTYPE html>
<html lang="en" class="auth-body">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="cache-control" content="max-age=0">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT">
<meta http-equiv="pragma" content="no-cache">
<link rel="stylesheet" type="text/css" th:href="${_styleHref}" href="p.css"/>
<title th:text="#{signup.title}">Signup</title>
<script type="text/javascript" th:utext="${_labels}">
/* TODO */
window.__labels = [];
window.__labels['UsernameRequired'] = "Vyplňte prosím e-mail";
window.__labels['PasswordRequired'] = "Vyplňte prosím heslo";
window.__labels['UnexpectedError'] = "Došlo k neočekávané chybě";
window.__labels['UsernameTaken'] = "Uživatel již existuje. Zvolte prosím jiný e-mail";
window.__labels['BadUsername'] = "Neplatný e-mail";
window.__labels['BadPassword'] = "Neplatné heslo";
window.__labels['BadUsernameOrPassword'] = "Neplatný e-mail nebo heslo";
</script>
</head>
<body class="auth-body">
<div class="auth-page">
<div class="auth-form">
<h1 class="small" th:text="#{signup.title}">Signup</h1>
<div id="signupError" class="auth-form-field-error" style="display:none;"></div>
<form id="signupForm">
<div id="signupErrorId"></div>
<div class="auth-form-input">
<input id="signupUsername" type="text" placeholder="username (email)" th:placeholder="#{signup.usernamePlaceholder}"/>
<div id="signupUsernameError" class="auth-form-field-error"></div>
</div>
<div class="auth-form-input">
<input id="signupPassword" type="password" placeholder="password" th:placeholder="#{signup.passwordPlaceholder}"/>
<div id="signupPasswordError" class="auth-form-field-error"></div>
</div>
<div class="auth-button-container">
<button id="signupButton" th:text="#{signup.signupAction}">Create new account</button>
<div id="authActivityIndicator" class="activity-indicator-container" style="display:none;">
<div class="ball-pulse">
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
<div>
<p class="message" th:utext="#{signup.disclaimer}"></p>
</div>
<div>
<p class="message">
<a href="/auth/login" th:text="#{signup.loginLink}">Login</a>
</p>
</div>
</form>
</div>
</div>
<script type="text/javascript" src="/utils.js"></script>
<script type="text/javascript" src="/auth/signup.js"></script>
<script defer="defer">
(function() {
signup.form({
formId: "signupForm",
usernameId: "signupUsername",
passwordId: "signupPassword"
});
})();
</script>
</body>
</html>
\ No newline at end of file
package com.jh.common.template;
import java.io.Writer;
import java.util.Locale;
import java.util.Map;
public interface TemplateService {
/**
* TODO.
*
* @param templateName the template name
* @param clazz the clazz
* @param data the data
* @param locale the locale
* @param writer the writer
*/
void evaluateTemplate(String templateName, Class<?> clazz, Map<String, Object> data, Locale locale, Writer writer);
/**
* TODO.
*
* @param template the template
* @param data the data
* @param locale the locale
* @param writer the writer
*/
void evaluateTemplate(String template, Map<String, Object> data, Locale locale, Writer writer);
}
\ No newline at end of file
/*
* Created on 19. 12. 2017 12:29:34
*
*/
package com.jh.common.utils;
import java.util.Base64;
import java.util.Base64.Encoder;
/**
* The Class DataURLUtils.
*
* @author Jan Hrabal
*/
public class DataURLUtils {
/** The encoder. */
private static Encoder encoder = Base64.getEncoder();
/**
* Instantiates a new data URL utils.
*/
private DataURLUtils() {
}
/**
* Png.
*
* @param image the image
* @return the string
*/
public static String png(byte[] image) {
return "data:image/png;base64," + encoder.encodeToString(image);
}
/**
* Jpg.
*
* @param image the image
* @return the string
*/
public static String jpg(byte[] image) {
return "data:image/jpeg;base64," + encoder.encodeToString(image);
}
}
/*
* Created on 27. 8. 2017 20:53:14
*
*/
package com.jh.common.utils;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.TemporalUnit;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;
/**
* The Class DateUtils.
*
* @author Jan Hrabal
*/
public class DateUtils {
//private Map<>
/**
* Start of.
*
* @param <T> the generic type
* @param t the t
* @param period the period
* @param unit the unit
* @return the t
*/
@SuppressWarnings("unchecked")
public static <T extends Temporal> T startOf(T t, int period, ChronoUnit unit) {
if (period > 1) {
t = unit.addTo(t, -period + 1);
}
if (unit == ChronoUnit.MONTHS) {
return (T) t.with(TemporalAdjusters.firstDayOfMonth());
} else if (unit == ChronoUnit.YEARS) {
return (T) t.with(TemporalAdjusters.firstDayOfYear());
}
return t;
}
/**
* End of.
*
* @param <T> the generic type
* @param t the t
* @param period the period
* @param unit the unit
* @return the t
*/
@SuppressWarnings("unchecked")
public static <T extends Temporal> T endOf(T t, int period, ChronoUnit unit) {
if (period > 1) {
t = startOf(t, period, unit);
t = unit.addTo(t, period);
}
if (unit == ChronoUnit.MONTHS) {
t = unit.addTo(t, -1);
return (T) t.with(TemporalAdjusters.lastDayOfMonth());
} else if (unit == ChronoUnit.YEARS) {
return (T) t.with(TemporalAdjusters.lastDayOfYear());
}
return t;
}
/**
* Start of quarter.
*
* @param year the year
* @param q the q
* @return the local date
*/
public static LocalDate startOfQuarter(int year, int q) {
if (q < 1 || q > 4) {
throw new IllegalArgumentException("Use quarter 1-4");
}
return LocalDate.of(year, q * 3 - 2, 1);
}
/**
* Months between.
*
* @param from the from
* @param to the to
* @return the long
*/
public static long monthsBetween(LocalDate from, LocalDate to) {
if (from == null || to == null) {
return 1;
}
Period p = Period.between(from, to.plusDays(1));
long months = Math.abs(p.toTotalMonths());
if (Math.abs(p.getDays()) > 1) {
months++;
}
return Math.max(months, 1);
}
/**
* Periods.
*
* @param from the from
* @param to the to
* @param unit the unit
* @return the list
*/
public static List<LocalDate> periods(LocalDate from, LocalDate to, TemporalUnit unit) {
if (from == null || to == null) {
return Collections.emptyList();
}
LinkedList<LocalDate> periods = new LinkedList<>();
LocalDate date = to;
int i = 1;
while (date.compareTo(from) > 0) {
periods.addFirst(date);
date = to.minus(i, unit).with(TemporalAdjusters.lastDayOfMonth());
i++;
}
return periods;
}
public static Date utcDate(LocalDate date, LocalTime time, TimeZone tz) {
if (tz == null) {
tz = TimeZone.getTimeZone("GMT");
}
ZonedDateTime zdt = ZonedDateTime.of(date, time, tz.toZoneId());
return Date.from(zdt.toInstant());
}
public static Date utcDate(LocalDate date, LocalTime time, String tzId) {
return utcDate(date, time, getTimeZone(tzId));
}
public static TimeZone getTimeZone(String id) {
if (id == null) {
return TimeZone.getTimeZone("GMT");
}
return TimeZone.getTimeZone(id);
}
}
/*
* Created on 2. 8. 2017 9:50:42
*
*/
package com.jh.common.utils;
import java.io.InputStream;
import java.util.Scanner;
/**
* The Class InputStreamUtils.
*
* @author Jan Hrabal
*/
public class InputStreamUtils {
/**
* Read content.
*
* @param stream the stream
* @return the string
*/
@SuppressWarnings("resource")
public static String readContent(InputStream stream) {
if (stream == null) {
return null;
}
try (InputStream is = stream) {
Scanner s = new Scanner(is).useDelimiter("\\A");
String content = s.hasNext() ? s.next() : null;
s.close();
return content;
} catch (Exception e) {
throw new IllegalStateException(String.format("Cannot read index file '%s'."), e);
}
}
}
package com.jh.common.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
import org.springframework.util.StringUtils;
//import javax.mail.internet.InternetAddress;
/**
* The Class Utils.
*/
public class Utils {
/** The Constant ONE_HUNDRED. */
public static final BigDecimal ONE_HUNDRED = new BigDecimal(100);
/**
* Parses the int.
*
* @param i the i
* @param defaultValue the default value
* @return the integer
*/
public static Integer parseInt(String i, Integer defaultValue) {
if (i == null) {
return defaultValue;
}
try {
return Integer.parseInt(i);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Checks if is true.
*
* @param o the o
* @return true, if is true
*/
public static boolean isTrue(Object o) {
if (o == null) {
return false;
}
if (o instanceof Boolean) {
return (Boolean) o;
}
String s = Objects.toString(o, "").toLowerCase();
return s.equals("true") || s.equals("t") || s.equals("yes") || s.equals("y") || s.equals("1");
}
/* */
/**
* Num.
*
* @param a the a
* @return the big decimal
*/
public static BigDecimal num(BigDecimal a) {
return a == null ? BigDecimal.ZERO : a;
}
/**
* Num.
*
* @param a the a
* @param fixed the fixed
* @return the big decimal
*/
public static BigDecimal num(BigDecimal a, int fixed) {
return a == null ? BigDecimal.ZERO : a.setScale(fixed, RoundingMode.HALF_UP);
}
/**
* Multiply.
*
* @param a the a
* @param b the b
* @return the big decimal
*/
public static BigDecimal multiply(BigDecimal a, BigDecimal b) {
return num(a).multiply(num(b));
}
/**
* Multiply.
*
* @param a the a
* @param b the b
* @param fixed the fixed
* @return the big decimal
*/
public static BigDecimal multiply(BigDecimal a, BigDecimal b, int fixed) {
return num(num(a, fixed).multiply(num(b, fixed)), fixed);
}
/**
* Divide.
*
* @param a the a
* @param b the b
* @return the big decimal
*/
public static BigDecimal divide(BigDecimal a, BigDecimal b) {
if (b == null || BigDecimal.ZERO.compareTo(b) == 0) {
return null;
}
return num(a).divide(b, RoundingMode.HALF_UP);
}
/**
* Divide.
*
* @param a the a
* @param b the b
* @param fixed the fixed
* @return the big decimal
*/
public static BigDecimal divide(BigDecimal a, BigDecimal b, int fixed) {
return num(num(a, fixed).divide(num(b, fixed), RoundingMode.HALF_UP), fixed);
}
/**
* Plus.
*
* @param a the a
* @param b the b
* @param fixed the fixed
* @return the big decimal
*/
public static BigDecimal plus(BigDecimal a, BigDecimal b, int fixed) {
return num(num(a, fixed).add(num(b, fixed)), fixed);
}
/**
* Minus.
*
* @param a the a
* @param b the b
* @param fixed the fixed
* @return the big decimal
*/
public static BigDecimal minus(BigDecimal a, BigDecimal b, int fixed) {
return num(num(a, fixed).subtract(num(b, fixed)), fixed);
}
/**
* Plus.
*
* @param a the a
* @param b the b
* @return the big decimal
*/
public static BigDecimal plus(BigDecimal a, BigDecimal b) {
return num(a).add(num(b));
}
/**
* Minus.
*
* @param a the a
* @param b the b
* @return the big decimal
*/
public static BigDecimal minus(BigDecimal a, BigDecimal b) {
return num(a).subtract(num(b));
}
/**
* Compare.
*
* @param a the a
* @param b the b
* @return the int
*/
public static int compare(BigDecimal a, BigDecimal b) {
return num(a).compareTo(num(b));
}
/**
* Sleep.
*
* @param millis the millis
*/
public static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* Round.
*
* @param n the n
* @param fixed the fixed
* @return the big decimal
*/
public static BigDecimal round(BigDecimal n, int fixed) {
if (n == null) {
return n;
}
return n.setScale(fixed, RoundingMode.HALF_UP);
}
/**
* Validate email.
*
* @param email the email
* @return true, if successful
*/
public static boolean validateEmail(String email) {
// try {
// new InternetAddress(email).validate();
// } catch (Exception e) {
// return false;
// }
return true;
}
public boolean hasText(String s) {
return StringUtils.hasText(s);
}
@SuppressWarnings("unchecked")
public static <T> int compareValues(Comparable<T> c1, Comparable<T> c2) {
if (c1 != null && c2 != null) {
return c1.compareTo((T) c2);
}
return 0;
}
@SuppressWarnings("unchecked")
public static <T> int compareValues(Comparable<T> c1, Comparable<T> c2, boolean nullsFirst) {
if (c1 != null && c2 != null) {
return c1.compareTo((T) c2);
}
if (c1 == c2) {
return 0;
}
return nullsFirst && c1 == null ? -1 : 1;
}
}
/*
* Created on 28. 7. 2017 15:26:38
*
*/
package com.jh.common.web;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.Scanner;
import java.util.function.Supplier;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
/**
* The Class ClientContent.
*
* @author Jan Hrabal
*/
public class ClientContent implements ResourceLoaderAware {
/** The Constant LOG. */
private static final Logger LOG = LoggerFactory.getLogger(ClientContent.class);
/** The response modifiers. */
private ClientContentModifier[] responseModifiers = new ClientContentModifier[0];
/** The use cache. */
private boolean useCache;
/** The cached content. */
private String cachedContent;
/** The path. */
private String path;
/** The resource loader. */
private ResourceLoader resourceLoader;
/** The input stream supplier. */
private Supplier<InputStream> inputStreamSupplier;
/**
* Instantiates a new client content.
*/
public ClientContent() {
super();
}
/**
* Instantiates a new client content.
*
* @param resourceLoader the resource loader
* @param path the path
* @param useCache the use cache
* @param responseModifiers the response modifiers
*/
public ClientContent(ResourceLoader resourceLoader, String path, boolean useCache, ClientContentModifier...responseModifiers) {
super();
this.resourceLoader = resourceLoader;
this.path = path;
this.useCache = useCache;
this.responseModifiers = responseModifiers;
inputStreamSupplier = this::fetchResource;
}
/**
* Instantiates a new client content.
*
* @param inputStreamSupplier the input stream supplier
* @param useCache the use cache
* @param responseModifiers the response modifiers
*/
public ClientContent(Supplier<InputStream> inputStreamSupplier, boolean useCache, ClientContentModifier...responseModifiers) {
super();
this.useCache = useCache;
this.responseModifiers = responseModifiers;
this.inputStreamSupplier = inputStreamSupplier;
if (responseModifiers != null) {
this.setResponseModifiers(responseModifiers);
}
}
/**
* Inits the.
*/
@PostConstruct
public void init() {
inputStreamSupplier = this::fetchResource;
}
/**
* Gets the content.
*
* @param request the request
* @return the content
*/
public String getContent(HttpServletRequest request) {
String content = useCache && cachedContent != null ? cachedContent : (cachedContent = readContent());
return evaluateIndexContent(content, request);
}
/**
* Reads HTML content from provided path (classpath).
*
* @return the string
*/
protected String readContent() {
Objects.requireNonNull(inputStreamSupplier, "Input stream loader is not initialized");
try (InputStream is = inputStreamSupplier.get()) {
@SuppressWarnings("resource")
Scanner s = new Scanner(is).useDelimiter("\\A");
String content = s.hasNext() ? s.next() : null;
s.close();
return content;
} catch (Exception e) {
throw new IllegalStateException(String.format("Cannot read index file '%s'.", path), e);
}
}
/**
* Calls configured {@link ClientContentModifier}s.
*
* @param content the content
* @param request the request
* @return the string
*/
protected String evaluateIndexContent(String content, HttpServletRequest request) {
for (ClientContentModifier cirm : responseModifiers) {
content = cirm.modifyResponse(content, request);
}
return content;
}
/**
* Sets the response modifiers.
*
* @param responseModifiers the new response modifiers
*/
public void setResponseModifiers(ClientContentModifier[] responseModifiers) {
this.responseModifiers = responseModifiers;
}
/**
* Sets the use cache.
*
* @param useCache the new use cache
*/
public void setUseCache(boolean useCache) {
this.useCache = useCache;
}
/**
* Sets the path.
*
* @param path the new path
*/
public void setPath(String path) {
this.path = path;
}
/**
* Sets the resource loader.
*
* @param arg0 the new resource loader
* @see org.springframework.context.ResourceLoaderAware#setResourceLoader(org.springframework.core.io.ResourceLoader)
*/
@Override
@Autowired(required = false)
public void setResourceLoader(ResourceLoader arg0) {
resourceLoader = arg0;
}
/**
* Fetch resource.
*
* @return the input stream
*/
protected InputStream fetchResource() {
if (resourceLoader == null) {
LOG.warn("There is no resource loader defined. TODO");
return null;
}
Resource resource = resourceLoader.getResource(path);
if (!resource.exists()) {
LOG.error(String.format("Cannot find resource '%s'", path));
return null;
}
try {
return resource.getInputStream();
} catch (IOException e) {
throw new RuntimeException(String.format("Cannot find resource '%s'", path), e);
}
};
/**
* Render.
*
* @param content the content
* @param response the response
*/
public static void render(String content, HttpServletResponse response) {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
try {
response.getWriter().print(content);
} catch (IOException e) {
throw new RuntimeException("Cannot render response", e);
}
response.setStatus(HttpServletResponse.SC_OK);
}
}
/*
* Created on 25. 7. 2017 9:53:03
*
*/
package com.jh.common.web;
import javax.servlet.http.HttpServletRequest;
/**
* Instances of this interface modify client response generated by {@link ClientContentFilter}.
*
* @author Jan Hrabal
*/
public interface ClientContentModifier {
/**
* Modifies response based on provided request .
*
* @param response current response
* @param request http request
* @return modified response
*/
public String modifyResponse(String response, HttpServletRequest request);
}
/*
* Created on 29. 1. 2018 13:40:45
*
*/
package com.jh.common.web;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.util.WebUtils;
/**
* The Class FallbackLocaleResolver.
*
* @author Jan Hrabal
*/
public class FallbackLocaleResolver extends AcceptHeaderLocaleResolver/* LocaleResolver*/ {
/** The locales string. */
@Value("${app.locales:en}")
private String localesString;
/** The default locale. */
@Value("${app.locale.default:en}")
private String defaultLocale;
/** The default country. */
@Value("${app.locale.defaultCountry:US}")
private String defaultCountry;
/** The locales. */
private Set<String> locales;
/**
* Inits the.
*/
@PostConstruct
public void init() {
locales = new LinkedHashSet<>();
if (localesString != null) {
String[] ls = localesString.split("[\\s,;]");
for (String s : ls) {
if (StringUtils.hasText(s)) {
locales.add(s.trim().toLowerCase());
}
}
} else {
locales.add("en");//TODO default config?
}
if (defaultLocale == null) {
defaultLocale = "en";
} else {
defaultLocale = defaultLocale.trim().toLowerCase();
}
}
/**
* Resolve locale.
*
* @param httpRequest the http request
* @return the locale
* @see org.springframework.web.servlet.LocaleResolver#resolveLocale(javax.servlet.http.HttpServletRequest)
*/
@Override
public Locale resolveLocale(HttpServletRequest httpRequest) {
String code = null;
//get param
String lang = httpRequest.getParameter("lang");
if (StringUtils.hasText(lang)) {
if (locales.contains(langCode(lang))) {
code = lang;
}
}
Cookie c = WebUtils.getCookie(httpRequest, "lang");
if (c != null) {
lang = c.getValue();
if (StringUtils.hasText(lang)) {
if (locales.contains(langCode(lang))) {
code = lang;
}
}
}
if (code == null) {
String acceptLanguage = httpRequest.getHeader("Accept-Language");
if (StringUtils.hasText(acceptLanguage)) {
String[] als = acceptLanguage.split(",");
for (String s : als) {
if (locales.contains(langCode(s))) {
code = s;
break;
}
}
}
}
String country = null;
if (code == null) {
code = defaultLocale;
country = defaultCountry;
} else {
country = countryCode(code);
code = langCode(code);
}
return country == null ? new Locale(code) : new Locale(code, country);
}
/**
* Sets the locale.
*
* @param arg0 the arg 0
* @param arg1 the arg 1
* @param arg2 the arg 2
* @see org.springframework.web.servlet.LocaleResolver#setLocale(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.util.Locale)
*/
@Override
public void setLocale(HttpServletRequest arg0, HttpServletResponse arg1, Locale arg2) {
// TODO do nothing
}
/**
* Lang code.
*
* @param s the s
* @return the string
*/
protected String langCode(String s) {
if (s == null) {
return defaultLocale;
}
s = s.trim().toLowerCase();
if (s.length() > 2) {
return s.substring(0, 2);
}
return s;
}
/**
* Lang name.
*
* @param s the s
* @return the string
*/
protected String langName(String s) {
if (s == null) {
return defaultLocale;
}
return s.trim().replace("-", "_");
}
/**
* Country code.
*
* @param s the s
* @return the string
*/
protected String countryCode(String s) {
if (s == null || s.length() < 5 ) {
return null;
}
return s.substring(3);
}
}
package com.jh.common.web;
import org.springframework.http.HttpStatus;
import org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* The Class GlobalControllerExceptionHandler.
*/
@ControllerAdvice
class GlobalControllerExceptionHandler {
/**
* Handle conflict.
*/
@ResponseStatus(HttpStatus.CONFLICT)
@ExceptionHandler(HibernateOptimisticLockingFailureException.class)
public void handleConflict() {
}
}
\ No newline at end of file
package com.jh.common.web;
import java.beans.PropertyEditorSupport;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
/**
* The Class LocalDateControllerAdvice.
*/
@ControllerAdvice
public class LocalDateControllerAdvice {
/**
* Inits the binder.
*
* @param binder the binder
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(LocalDate.parse(text, DateTimeFormatter.ISO_DATE));
}
});
}
}
\ No newline at end of file
package com.jh.common.web;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module.Feature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.jh.common.web.list.PagingArgumentResolver;
import com.jh.common.web.list.SortingArgumentResolver;
/**
* The Class ReactApplicationContext.
*/
public class ReactApplicationContext implements WebMvcConfigurer {
/**
* @param converters
* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter#configureMessageConverters(java.util.List)
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper());
converters.add(converter);
}
/**
* Object mapper.
*
* @return the object mapper
*/
@Bean
public ObjectMapper objectMapper() {
ObjectMapper om = new ObjectMapper();
om.setSerializationInclusion(Include.NON_NULL);
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
Hibernate5Module module = new Hibernate5Module();
module.disable(Feature.USE_TRANSIENT_ANNOTATION);
module.enable(Feature.REPLACE_PERSISTENT_COLLECTIONS);
om.registerModule(module);
return om;
}
/**
* @param argumentResolvers
* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter#addArgumentResolvers(java.util.List)
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new PagingArgumentResolver());
argumentResolvers.add(new SortingArgumentResolver());
}
/**
* Locale resolver.
*
* @return the locale resolver
*/
@Bean
@Primary
public LocaleResolver localeResolver() {
return new FallbackLocaleResolver();
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
/**
* @param configurer
* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter#configurePathMatch(org.springframework.web.servlet.config.annotation.PathMatchConfigurer)
*/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
// .setUseTrailingSlashMatch(false)
// .setUseRegisteredSuffixPatternMatch(true)
// .setPathMatcher(antPathMatcher())
// .setUrlPathHelper(urlPathHelper());
}
}
package com.jh.common.web.error;
import java.util.Collection;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* The Class BadRequestException.
*/
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class BadRequestException extends RestApiException {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/**
* Instantiates a new bad request exception.
*
* @param errors the errors
*/
public BadRequestException(Collection<ErrorMessage> errors) {
super(HttpStatus.BAD_REQUEST, errors);
}
/**
* Instantiates a new bad request exception.
*
* @param errors the errors
*/
public BadRequestException(ErrorMessage... errors) {
super(HttpStatus.BAD_REQUEST, errors);
}
}
package com.jh.common.web.error;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* The Class ErrorMessage.
*/
public class ErrorMessage {
/** The field. */
private String field;
/** The code. */
private String code;
/** The message. */
@JsonProperty("msg")
private String message;
/**
* Instantiates a new error message.
*/
private ErrorMessage() {
}
/* builder methods */
/**
* Constructs error message with field, code and message .
*
* @param field the field
* @param code the code
* @param message the message
* @return the error message
*/
public static ErrorMessage withFieldCodeMessage(String field, String code, String message) {
ErrorMessage em = new ErrorMessage();
em.field = field;
em.code = code;
em.message = message;
return em;
}
/**
* Constructs error message with field, code and message .
*
* @param field the field
* @param message the message
* @return the error message
*/
public static ErrorMessage withFieldMessage(String field, String message) {
ErrorMessage em = new ErrorMessage();
em.field = field;
em.message = message;
return em;
}
/**
* Constructs error message with field and code .
*
* @param field the field
* @param code the code
* @return the error message
*/
public static ErrorMessage withFieldCode(String field, String code) {
ErrorMessage em = new ErrorMessage();
em.field = field;
em.code = code;
return em;
}
/**
* Constructs error message with code and message .
*
* @param code the code
* @param message the message
* @return the error message
*/
public static ErrorMessage withCodeMessage(String code, String message) {
ErrorMessage em = new ErrorMessage();
em.code = code;
em.message = message;
return em;
}
/**
* With message.
*
* @param message the message
* @return the error message
*/
public static ErrorMessage withMessage(String message) {
ErrorMessage em = new ErrorMessage();
em.message = message;
return em;
}
/**
* With code.
*
* @param code the code
* @return the error message
*/
public static ErrorMessage withCode(String code) {
ErrorMessage em = new ErrorMessage();
em.message = code;
return em;
}
/* getters */
/**
* Gets the field.
*
* @return the field
*/
public String getField() {
return field;
}
/**
* Gets the code.
*
* @return the code
*/
public String getCode() {
return code;
}
/**
* Gets the message.
*
* @return the message
*/
public String getMessage() {
return message;
}
}
\ No newline at end of file
/*
* Created on 9. 10. 2017 14:36:28
*
*/
package com.jh.common.web.error;
/**
* The Interface ErrorMessages.
*
* @author Jan Hrabal
*/
public interface ErrorMessages {
/**
* Gets the error messages.
*
* @return the error messages
*/
public ErrorMessage[] getErrorMessages();
}
/*
* Created on 9. 11. 2017 10:26:30
*
*/
package com.jh.common.web.error;
import java.util.Collection;
/**
* The Class ErrorMessagesException.
*
* @author Jan Hrabal
*/
public abstract class ErrorMessagesException extends RuntimeException implements ErrorMessages {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The errors. */
private ErrorMessage[] errors;
/**
* Instantiates a new error messages exception.
*
* @param errors the errors
*/
public ErrorMessagesException(ErrorMessage...errors) {
super();
this.errors = errors;
}
/**
* Instantiates a new error messages exception.
*
* @param errors the errors
*/
public ErrorMessagesException(Collection<ErrorMessage> errors) {
super();
ErrorMessage[] errs = new ErrorMessage[0];
this.errors = errors == null ? errs : errors.toArray(errs);
}
/**
* Gets the error messages.
*
* @return the error messages
* @see de.dhl.spms.api.ErrorMessages#getErrorMessages()
*/
@Override
public ErrorMessage[] getErrorMessages() {
return errors;
}
}
package com.jh.common.web.error;
import org.springframework.http.HttpStatus;
/**
* The Interface ErrorStatus.
*/
public interface ErrorStatus {
/**
* Gets the error status.
*
* @return the error status
*/
HttpStatus getErrorStatus();
}
package com.jh.common.web.error;
import java.util.Collection;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* The Class NotFoundException.
*/
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NotFoundException extends RestApiException {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/**
* Instantiates a new not found exception.
*
* @param errors the errors
*/
public NotFoundException(Collection<ErrorMessage> errors) {
super(HttpStatus.NOT_FOUND, errors);
}
/**
* Instantiates a new not found exception.
*
* @param errors the errors
*/
public NotFoundException(ErrorMessage... errors) {
super(HttpStatus.NOT_FOUND, errors);
}
}
/*
* Created on 24. 1. 2018 8:46:58
*
*/
package com.jh.common.web.error;
import java.util.Collection;
import org.springframework.http.HttpStatus;
/**
* The Class RestApiException.
*
* @author Jan Hrabal
*/
public class RestApiException extends ErrorMessagesException implements ErrorStatus {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The status. */
private HttpStatus status;
/**
* Instantiates a new rest api exception.
*
* @param status the status
* @param errors the errors
*/
public RestApiException(HttpStatus status, ErrorMessage... errors) {
super(errors);
this.status = status;
}
/**
* Instantiates a new rest api exception.
*
* @param status the status
* @param errors the errors
*/
public RestApiException(HttpStatus status, Collection<ErrorMessage> errors) {
super(errors);
this.status = status;
}
/**
* Gets the error status.
*
* @return the error status
* @see com.jh.common.web.error.ErrorStatus#getErrorStatus()
*/
@Override
public HttpStatus getErrorStatus() {
return status;
}
}
package com.jh.common.web.error;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
/**
* The Class RestResponseEntityExceptionHandler.
*/
@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
/**
* Handle all.
*
* @param ex the ex
* @return the response entity
*/
@ExceptionHandler({ Exception.class })
public ResponseEntity<Object> handleAll(Exception ex) {
ErrorMessage[] errors = null;
if (ex instanceof ErrorMessages) {
errors = ((ErrorMessages) ex).getErrorMessages();
}
if (errors == null) {
errors = new ErrorMessage[] { ErrorMessage.withCodeMessage("error.internal", ex.getMessage()) };
}
HttpStatus status = null;
if (ex instanceof ErrorStatus) {
status = ((ErrorStatus) ex).getErrorStatus();
}
if (status == null) {
ResponseStatus responseStatus = ex.getClass().getAnnotation(ResponseStatus.class);
if (responseStatus != null) {
status = responseStatus.value();
}
}
if (status == null) {
status = HttpStatus.INTERNAL_SERVER_ERROR;
//log unhandled exception
ex.printStackTrace();
}
return new ResponseEntity<>(errors, new HttpHeaders(), status);
}
}
\ No newline at end of file
/*
* Created on 25. 8. 2017 18:29:24
*
*/
package com.jh.common.web.list;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The Interface DefaultSorting.
*
* @author Jan Hrabal
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.PARAMETER})
public @interface DefaultSorting {
/**
* Field.
*
* @return the string
*/
String field();
/**
* Trend.
*
* @return the sort trend
*/
SortTrend trend() default SortTrend.ASCENDING;
}
package com.jh.common.web.list;
/**
* Default implementation of {@link SortingInfo}.
*
* @author jh
*/
public class DefaultSortingInfo implements SortingInfo {
/** The field. */
private String field;
/** The trend. */
private SortTrend trend;
/**
* Instantiates a new default sorting info.
*
* @param field the field
* @param trend the trend
*/
public DefaultSortingInfo(String field, SortTrend trend) {
super();
this.field = field;
this.trend = trend;
}
/**
* @return
* @see com.jh.common.web.list.SortingInfo#getField()
*/
@Override
public String getField() {
return field;
}
/**
* @return
* @see com.jh.common.web.list.SortingInfo#getTrend()
*/
@Override
public SortTrend getTrend() {
return trend;
}
/**
* Asc.
*
* @param field the field
* @return the sorting info
*/
public static final SortingInfo asc(String field) {
return new DefaultSortingInfo(field, SortTrend.ASCENDING);
}
/**
* Desc.
*
* @param field the field
* @return the sorting info
*/
public static final SortingInfo desc(String field) {
return new DefaultSortingInfo(field, SortTrend.DESCENDING);
}
}
\ No newline at end of file
/*
* Created on 3. 11. 2016 9:19:23
*
*/
package com.jh.common.web.list;
import java.util.List;
import java.util.Objects;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.NativeWebRequest;
import com.jh.common.utils.Utils;
/**
* The Class ListHelper.
*/
public class ListHelper {
/** The Constant DEFAULT_PAGE_SIZE. */
public static final int DEFAULT_PAGE_SIZE = 20;
/**
* Read paging info.
*
* @param request the request
* @param defaultField the default field
* @param defaultTrend the default trend
* @return the paging info
*/
public static PagingInfo readPagingInfo(NativeWebRequest request, String defaultField, SortTrend defaultTrend) {
if (request == null) {
return new DefaultPagingInfo();
}
Integer page = 0;
Integer pageSize = 10;
if (Utils.isTrue(request.getHeader("X-Paging"))) {
String s = request.getHeader("X-Paging-Page");
page = Utils.parseInt(s, 0);
s = request.getHeader("X-Paging-PageSize");
pageSize = Utils.parseInt(s, 10);
}
SortingInfo si = readSortingInfo(request, defaultField, defaultTrend);
return si == null ? new DefaultPagingInfo(page, pageSize) : new DefaultPagingInfo(page, pageSize, si.getField(), si.getTrend());
}
public static PagingInfo sortedPagingInfo(PagingInfo pi, String sortField, SortTrend trend) {
return new DefaultPagingInfo(pi.getPage(), pi.getPageSize(), sortField, trend);
}
/**
* Read paging info.
*
* @param request the request
* @return the paging info
*/
public static PagingInfo readPagingInfo(NativeWebRequest request) {
return readPagingInfo(request, null, null);
}
/**
* Read sorting info.
*
* @param request the request
* @param defaultField the default field
* @param defaultTrend the default trend
* @return the sorting info
*/
public static SortingInfo readSortingInfo(NativeWebRequest request, String defaultField, SortTrend defaultTrend) {
if (request == null) {
return null;
}
if (!(Utils.isTrue(request.getHeader("X-Sorting")) || defaultField != null)) {
return null;
}
String field = null;
String trend = null;
SortTrend st = null;
field = request.getHeader("X-Sorting-Field");
trend = request.getHeader("X-Sorting-Trend");
if (!StringUtils.hasText(field) && StringUtils.hasText(defaultField)) {
field = defaultField;
}
if (trend == null) {
if (defaultTrend != null) {
st = defaultTrend;
}
} else {
st = "desc".equalsIgnoreCase(trend) ? SortTrend.DESCENDING : SortTrend.ASCENDING;
}
if (!StringUtils.hasText(field) || st == null) {
return null;
}
return new DefaultSortingInfo(field, st);
}
/**
* Write paging info.
*
* @param page the page
* @return the http headers
*/
public static HttpHeaders writePagingInfo(Page<?> page) {
return writePagingInfo(new HttpHeaders(), page);
}
/**
* Writes complete headers for provided page .
*
* @param headers the headers
* @param page the page
* @return the http headers
*/
public static HttpHeaders writePagingInfo(HttpHeaders headers, Page<?> page) {
if (page == null) {
return headers;
}
headers.add("X-Paging", "true");
headers.add("X-Paging-Page", String.valueOf(page.getPage()));
headers.add("X-Paging-PagesCount", String.valueOf(page.getPagesCount()));
writeSortingInfo(headers, page);
return headers;
}
/**
* Writes only sorting information for provided page.
*
* @param headers the headers
* @param page the page
* @return the http headers
*/
public static HttpHeaders writeSortingInfo(HttpHeaders headers, Page<?> page) {
if (page == null || !page.isSorted()) {
return headers;
}
headers.add("X-Sorting", Objects.toString(page.isSorted()));
if (page.isSorted()) {
headers.add("X-Sorting-Field", page.getSortBy());
headers.add("X-Sorting-Trend", page.getTrend() == SortTrend.DESCENDING ? "desc" : "asc");
}
return headers;
}
/**
* Creates paged response entity.
*
* @param <T> the generic type
* @param page the page
* @return the response entity
*/
public static <T> ResponseEntity<List<T>> pagedResponse(Page<T> page) {
return new ResponseEntity<>(page.getContent(), ListHelper.writePagingInfo(page), HttpStatus.OK);
}
}
/**
* Default implementation for {@link PagingInfo}
*
* @author Jan Hrabal
*/
class DefaultPagingInfo implements PagingInfo {
private int pageSize;
private int page;
private String field;
private SortTrend sortTrend;
public DefaultPagingInfo() {
this(null, null);
}
public DefaultPagingInfo(Integer page, Integer pageSize) {
this(page, pageSize, null, null);
}
public DefaultPagingInfo(Integer page, Integer pageSize, String field, SortTrend sortTrend) {
super();
this.pageSize = pageSize == null || pageSize < 1 ? 20 : pageSize;
this.page = page == null ? 0 : page;
this.field = field;
this.sortTrend = sortTrend;
}
@Override
public int getPageSize() {
return pageSize;
}
@Override
public int getPage() {
return page;
}
@Override
public String getField() {
return field;
}
@Override
public SortTrend getTrend() {
return sortTrend;
}
}
\ No newline at end of file
/*
* Created on 12. 10. 2016 13:01:20
*
*/
package com.jh.common.web.list;
import java.util.Collections;
import java.util.List;
/**
* TODO.
*
* @author Jan Hrabal
* @param <T> the generic type
*/
public class Page<T> {
/** The content. */
private List<T> content;
/** The pages count. */
private int pagesCount;
/** The page. */
private int page;
/** The sort by. */
private String sortBy;
/** The trend. */
private SortTrend trend;
/**
* Instantiates a new page.
*
* @param content the content
*/
public Page(List<T> content) {
this(0, 1, content);
}
/**
* Instantiates a new page.
*
* @param page the page
* @param pagesCount the pages count
* @param content the content
*/
public Page(int page, int pagesCount, List<T> content) {
this.content = content;
this.page = page;
this.pagesCount = pagesCount;
}
/**
* Instantiates a new page.
*
* @param page the page
* @param pagesCount the pages count
* @param content the content
* @param sortBy the sort by
* @param trend the trend
*/
public Page(int page, int pagesCount, List<T> content, String sortBy, SortTrend trend) {
super();
this.content = content;
this.pagesCount = pagesCount;
this.page = page;
this.sortBy = sortBy;
this.trend = trend;
}
/**
* Gets the content.
*
* @return the content
*/
public List<T> getContent() {
return content == null ? Collections.emptyList() : content;
}
/**
* Gets the pages count.
*
* @return the pages count
*/
public int getPagesCount() {
return pagesCount;
}
/**
* Gets the page.
*
* @return the page
*/
public int getPage() {
return page;
}
/**
* Gets the sort by.
*
* @return the sort by
*/
public String getSortBy() {
return sortBy;
}
/**
* Gets the trend.
*
* @return the trend
*/
public SortTrend getTrend() {
return trend;
}
/**
* Checks if is sorted.
*
* @return true, if is sorted
*/
public boolean isSorted() {
return sortBy != null && trend != null;
}
}
package com.jh.common.web.list;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* The Class PagingArgumentResolver.
*/
public class PagingArgumentResolver implements HandlerMethodArgumentResolver {
/**
* @param parameter
* @return
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(PagingInfo.class);
}
/**
* @param parameter
* @param mavContainer
* @param webRequest
* @param binderFactory
* @return
* @throws Exception
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory)
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
DefaultSorting defaultSorting = parameter.getMethodAnnotation(DefaultSorting.class);
if (defaultSorting == null) {
defaultSorting = parameter.getParameterAnnotation(DefaultSorting.class);
}
if (defaultSorting != null) {
return ListHelper.readPagingInfo(webRequest, defaultSorting.field(), defaultSorting.trend());
}
return ListHelper.readPagingInfo(webRequest);
}
}
\ No newline at end of file
/*
* Created on 13. 10. 2016 17:11:34
*
*/
package com.jh.common.web.list;
/**
* TODO.
*
* @author Jan Hrabal
*/
public interface PagingInfo extends SortingInfo {
/**
* Gets the page size.
*
* @return the page size
*/
int getPageSize();
/**
* Gets the page.
*
* @return the page
*/
int getPage();
}
/*
* Created on 30. 10. 2016 7:32:25
*
*/
package com.jh.common.web.list;
/**
* The Enum SortTrend.
*
* @author Jan Hrabal
*/
public enum SortTrend {
/** The ascending. */
ASCENDING,
/** The descending. */
DESCENDING
}
package com.jh.common.web.list;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import com.jh.common.utils.Utils;
/**
* The Class SortingArgumentResolver.
*/
public class SortingArgumentResolver implements HandlerMethodArgumentResolver {
/*
const ps = headers["x-paging-page"] * 1;
const psize = headers["x-paging-pagesize"] * 1;
const pcs = headers["x-paging-pagescount"] * 1;
*/
/** The Constant PAGING_PAGE. */
private static final String PAGING_PAGE = "X-Paging-Page";
/** The Constant PAGING_PAGE_SIZE. */
private static final String PAGING_PAGE_SIZE = "X-Paging-PageSize";
// private static final String PAGING_PAGES_COUNT = "X-Paging-PagesCount";
/**
* @param parameter
* @return
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(PagingInfo.class);
}
/**
* @param parameter
* @param mavContainer
* @param webRequest
* @param binderFactory
* @return
* @throws Exception
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory)
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Integer page = Utils.parseInt(webRequest.getHeader(PAGING_PAGE), 0);
Integer pageSize = Utils.parseInt(webRequest.getHeader(PAGING_PAGE_SIZE), 0);
return new DefaultPagingInfo(page, pageSize);
}
}
\ No newline at end of file
/*
* Created on 13. 10. 2016 17:11:34
*
*/
package com.jh.common.web.list;
/**
* TODO.
*
* @author Jan Hrabal
*/
public interface SortingInfo {
/**
* Gets the field.
*
* @return the field
*/
String getField();
/**
* Gets the trend.
*
* @return the trend
*/
SortTrend getTrend();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment