A simple web application for finding next colliding holidays for given countries and date.
It uses the Holiday API underneath.
These instructions will get the project up and running on your local machine for development and testing purposes.
Project requires Java 10 to run and JDK 10 with Gradle to build.
In order to build the application, simply issue:
gradle build
In order to build a Docker image issue:
gradle docker
gradle bootRun
java -jar build/libs/bs-holidays.jar
The resulting artifact is an executable file so it can be run like any other binary or script file:
build/libs/bs-holidays.jar
After building the docker image start the application by issuing:
docker run -p 8080:8080 -v `pwd`/logs:/logs cleankod/bs-holidays
The application is going to be available on port 8080
and its logs are going to be available in ./logs
directory.
application.supported-countries
- coma separated list of countries supported for colliding holidays lookup.application.holiday-api.key
- an API key value for the Holiday API.application.holiday-api.base-url
- base URL for the Holiday API.application.only-past-supported
- aboolean
value that states whether only past dates are supported. Keep in mind though, that when set totrue
the Premium account key for the Holiday API has to be provided. Otherwise the application will issue an error on each request when present or future dates are given. When set tofalse
the application will only allow dates one month before the current one.application.holiday-api.timeout-duration
- a client timeout duration in ISO-8601 format.
Endpoint:
GET /colliding-holidays
Parameters:
date
(required) - start the lookup from this day oncountries
(required) - look for holidays in these countries (one or more, as long as they are supported by the application)
Sample request:
http://localhost:8080/colliding-holidays?date=2017-12-03&countries=PL&countries=NO
Will produce:
{
"holidays": [
{
"country": "NO",
"date": "2017-12-25",
"name": "1. juledag"
},
{
"country": "PL",
"date": "2017-12-25",
"name": "Pierwszy dzień Bożego Narodzenia"
}
]
}
Which means that starting from 2017-12-03
the first colliding holiday for Poland and Norway is on 2017-12-25
.
Endpoint:
GET /supported-countries
Returns a list of countries supported by the application.
As for the HTTP endpoints, monitoring runs on a different port (8081) for security purposes. Some endpoints to try out:
- /health - shows application health information.
- /info - shows application name and version. The version number is determined and set at build time so it may show a placeholder when started in IDE.
For more information about configuration and a complete list of endpoints, please refer to this guide.
Each API request causes the correlationId
to be generated and added to all log lines initiated by such request. This eases looking for potential problems in the application. The correlationId
can be found at the begging of each log line. If the value [none]
is present it simply means that the log entry was not caused by the API request.
By default, the application logs into three files:
web-trace.log
- all requests made to the APIholiday-client-requests-trace.log
- all requests made to the Holiday APIapplication.log
- application specific logs
- Spring Boot - The web framework
- Gradle - Dependency Management and build tool
- Feign - Java HTTP client binder
- Hazelcast - Cache manager
- Spock Framework - Testing and specification framework
- Jadler - HTTP mock fot testing purposes
- Project Lombok - Less boilerplate code
- Spring Boot Actuator - For monitoring Spring Boot Application
Black-box testing is mostly used in order to favor refactoring. It is much simpler to completely change the underlying encapsulation of an use case without changing the tests. Moreover, the application is tested end-to-end, also with the exposed contract (API).
Only the BaseMvcSpec
contains Spring-specific stuff. This approach eases the migration to other potential framework or toolset. The whole specification for the project stays the same.
Wherever possible, no Spring-specific stuff was used inside of actual modules (in exception of controllers and the application
module). This also eases potential framework change or upgrade. The framework upgrade could also be more seamless for all of those changes that are not backwards compatible because framework specific stuff is kept in one place and the business logic is not polluted.
Package scoped access is used whenever possible to encapsulate the internal classes of a module. There is only one exception to this rule - the component with cached results. It has to be wrapped in a AOP-proxy in order to enable caching in a painless way. It has to be therefore declared public in order to enable its construction in the main application class.
In order not to stress the actual Holiday API servers, the mocked responses are used for black-box testing. It also makes the tests complete faster.
Some given parameters may stress the Holiday API servers too much due to many requests sent in order to find colliding holidays. That is why a caching abstraction with a distributable cache manager are used.
Bean Validation seamed an overkill here, hence the syntactic validation is done in the constructors. Beside keeping it simple, another advantage is that there is no way that an invalid value object would exist in runtime.
There are few things still pending for improvement:
- the holiday fetching logic may be looking for whole months instead of day-by-day requests, and then find colliding holidays in such results
- provide a documentation fot the API (OpenAPI or JSON-doc).
- Hystrix tuning and monitoring tools (ie. for Actuator).
Spring 5 with Java 9+ will emmit the following warnings on build time:
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.springframework.cglib.core.ReflectUtils$1
(file:~/.gradle/caches/modules-2/files-2.1/org.springframework/spring-core/5.0.6.RELEASE/*/spring-core-5.0.6.RELEASE.jar)
to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of org.springframework.cglib.core.ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
This issue has been already reported and is being tracked as: SPR-15859. It does not, however, seem to have any significant impact on the application's runtime.