This project contains the general-purpose tools to spring. Project is licensed under Apache License 2.0.
Consult the proyect for details on the current spring-commons roadmap.
Functionality of this package is contained in Java package com.github.damianwajser
, and can be used using following Maven dependency:
<properties>
...
<!-- Use the latest version whenever possible. -->
<spring.commons>{lastversion}</spring.commons>
...
</properties>
<dependencies>
...
<dependency>
<groupId>com.github.damianwajser</groupId>
<artifactId>spring-commons</artifactId>
<version>${spring.commons}</version>
</dependency>
...
</dependencies>
compile 'com.github.damianwajser:spring-commons:{lastVersion}'
@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.github.damianwajser","{YOUR-PACKAGE}"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Key | Module | Reference |
---|---|---|
PropertyNamingStrategy.SnakeCaseStrategy | Spring web | format JSON response when Object is return in a controller |
Collection of exceptions for the most common cases when rest apis are built, and override the http code. In addition, they request additional information for errors. They can be used on their own, or they are caught by spring-commons-exception-handler and this information is used to generate a nice error message. For example:
Exception | Http Code |
---|---|
ForbiddenException | 403 |
PermissionDeniedException | 403 |
BadRequestException | 400 |
NotFoundException | 404 |
ConflictException | 409 |
Override all annotations for standard JSR annotations:
@NotNull – validates that the annotated property value is not null
@AssertTrue – validates that the annotated property value is true
@Size – validates that the annotated property value has a size between the attributes min and max; can be applied to String, Collection, Map, and array properties
@Min – vValidates that the annotated property has a value no smaller than the value attribute
@Max – validates that the annotated property has a value no larger than the value attribute
@Email – validates that the annotated property is a valid email address
@NotEmpty – validates that the property is not null or empty; can be applied to String, Collection, Map or Array values
@NotBlank – can be applied only to text values and validated that the property is not null or whitespace
@Positive and @PositiveOrZero – apply to numeric values and validate that they are strictly positive, or positive including 0
@Negative and @NegativeOrZero – apply to numeric values and validate that they are strictly negative, or negative including 0
@Past and @PastOrPresent – validate that a date value is in the past or the past including the present; can be applied to date types including those added in Java 8
@Future and @FutureOrPresent – validates that a date value is in the future, or in the future including the present
On the other hand some useful validations are added:
@CardToken -
@CardExpiration -
Some annotations accept additional attributes, but the message and the bussisness code attributes are common to all of them. message: This is the message that will usually be rendered when the value of the respective property fails validation. bussiness code: this the code that will usualle for spring-commons-exception-handler and generate a prettty message.
Key | Posible Value | Reference | Default Value |
---|---|---|---|
logging.pattern. level | "Request ID: %X{requestId} Client IP: %X{clientIp}" | log pattern | Empty |
logstash. appName | ms-users | the name of microservice | test |
logstash. destination | localhost:5000 | host and port of logstash server | localhost:5000 |
logstash. trace.id.key | any string | Header key from get the request Id if is empty generate a new UUID to replace RequestId | UUID |
logstash. duration. request.enabled | true/false | For each request log the duration. | false |
This module tries to solve the problems associated with idempotence. For them, create a filter within the spring chain of responsibilities. When the first request is made, it saves in redis the request sent by the client associated with an idempotence key. When another request is made two things can happen:
- The first request finished executing, which returns the same response that was obtained in the first call.
- In case the first request is still running, a message will be returned indicating the conflict.
This configuration is done by registering some beans and properties, you can see the following example:
Key | Posible Value | Reference | Default Value |
---|---|---|---|
spring.commons.idempotency.enabled | true/false | Enable the module | false |
spring.commons.idempotency.message | Any String | ||
spring.commons.idempotency.ttl | Any nunmber | ||
spring.commons.idempotency.badrequest.code | Any String | 400 | |
spring.commons.idempotency.conflict.code | Any String | 409 | |
spring.commons.idempotency.conflict.mesasge | Any String | idempotency key is bussy |
@Configuration
public class IdempotencyConfiguration {
@Bean
public IdempotencyEndpoints idempotencyEndpoints() {
IdempotencyEndpoints idempotencyEndpoints = new IdempotencyEndpoints();
// register endpoint by all Http Methods and generic Key generator
// (The idempotence key is generated based on the header sent by the client, X-Idempotency-Key)
idempotencyEndpoints.addIdempotencyEndpoint("/idempotency_generic");
//Customize another enpoint only by POST method and custom keyGenerator
idempotencyEndpoints.addIdempotencyEndpoint("/idempotency_by_custom", new FooIdempotencyKeyGenerator(), HttpMethod.POST);
return idempotencyEndpoints;
}
}
Eneable the module
spring.commons.idempotency.enabled=true
Remember that this module works with redis, with which you should first configure your redis, for which I leave you an example:
- Set a Redis Properties in application.properties file
spring.redis.host=localhost
spring.redis.port=6379
- Create a Redis ConnectionFactory, in this case I choose the jedis connector.
@Configuration
public class RedisConfiguration {
@Bean
public RedisConnectionFactory redisConnectionFactory(RedisProperties redisProperties) {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(redisProperties.getRedisHost(), redisProperties.getRedisPort());
JedisClientConfiguration clientConfiguration = JedisClientConfiguration.builder().readTimeout(Duration.ofMillis(0)).connectTimeout(Duration.ofMillis(0)).build();
return new JedisConnectionFactory(config, clientConfiguration);
}
}
- Suppose we have the following domain object
public class FooObject {
private String value;
//The Empty Constructor is required
public FooObject(){ }
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
- So we want some properties of the object to be part of the idempotence key, for which we should create our own KeyGenerator and override the "generateKey" method. The declaration of the generics is important, since the request will be stopped and a mapping will be made towards the declared object, it can return InternalErrorOfServer in case of a ClassCastException.
//very important
//generic
public class FooIdempotencyKeyGenerator<T> implements IdempotencyKeyGenerator<FooObject> {
private static final String IDEMPOTENCY_DEFALUT_HEADER = "X-Idempotency-Key";
@Override
public String generateKey(HttpHeaders headers, HttpMethod method, String path, FooObject request) {
String key = getHeaderValue(headers, IDEMPOTENCY_DEFALUT_HEADER);
return path + "-" + key + "-" + method.toString() + "-" + request.getValue();
}
protected String getHeaderValue(HttpHeaders headers, String headerKey) {
List<String> idempotencyHeader = headers.get(headerKey);
String key;
if (idempotencyHeader != null) {
key = idempotencyHeader.stream().collect(Collectors.joining("-"));
} else {
// ArgumentNotFoundException is used to return a bad request indicating that the field is mandatory
throw new ArgumentNotFoundException(headerKey);
}
return key;
}
}
The Spring Framework is released under version 2.0 of the Apache License.