GithubHelp home page GithubHelp logo

oswaldobapvicjr / confectory Goto Github PK

View Code? Open in Web Editor NEW
9.0 2.0 4.0 1.84 MB

The modular configuration framework for Java applications

License: Apache License 2.0

Java 100.00%
java configuration properties json xml yaml json-parser configuration-management jsonpath yaml-parser toml ini deserialization java-library

confectory's Introduction

confectory-logo

Contributors GitHub Workflow Status Coverage License Maven Central Javadoc

The modular, multi-format configuration framework for Java applications.


Overview

Confectory is a modular framework designed to hide the complexity of handling multiple configuration files for Java applications in general, providing a reliable and fast API for parsing data from different sources (file or URL) in a variety of formats, and allowing data access using a unified query language.

flowchart TD
    User(["User"]) -->|"property"| c((("`**confectory**`"))):::larger
    User -->|"xpath"| c
    User -->|"jsonpath"| c

    c-->properties("properties"):::condensed
    c-->ini("INI"):::expanded
    c-->xml("XML"):::expanded
    c-->json("JSON"):::expanded
    c-->yaml("YAML"):::expanded
    c-->toml("TOML"):::expanded

    classDef larger font-size:18pt
    classDef condensed letter-spacing:-0.8px
    classDef expanded letter-spacing:1.2px
Loading

Features

  • Easy configuration setup via intuitive API
  • Simple data query using JSONPath, XPath, or user-defined beans
  • Support for the best providers available in the community (e.g.: Jackson, GSON)
  • Multiple configuration formats (e.g.: XML, JSON, or YAML) with custom precedence levels
  • Lazy loading of configuration sources

Examples

1. Load data from a local Properties file in the classpath:

flowchart LR
  subgraph Configuration
    direction LR
    object["☕ Properties"]
  end
  u(["User"]) -- 1️⃣ build --> Configuration
  object -- 2️⃣ load --> file["📄 application.properties"]
  u -- 3️⃣ get...(key) --> Configuration
Loading
Configuration config = Configuration.builder()
        .source("classpath://application.properties")
        .build();

Then access document data using one of the getter methods, using keys:

System.out.println(config.getBoolean("web.enable"));

2. Load data from a JSON document in a Web server:

flowchart LR
  subgraph Configuration
    direction LR
    object["⭕ JSONObject"]
  end
  u(["User"]) -- 1️⃣ build --> Configuration
  object -- 2️⃣ load --> file["🌍 http://time.jsontest.com"]
  u -- 3️⃣ get...(jsonpath) --> Configuration
Loading
Configuration<JSONObject> config = Configuration.<JSONObject>builder()
        .source("http://time.jsontest.com")
        .mapper(new JSONObjectMapper())
        .build();

Then access document data using JSONPath expressions:

System.out.println(config.getString("$.time"));

ℹ️ Find more examples in the wiki.

How to include it

Confectory was designed to work with the lowest possible number of transitive dependencies. So, we offer separate modules that can be selected according to the client's needs, optimizing your application:

Module Providers Properties XML JSON YAML TOML
confectory-core Java + json-smart
confectory-datamapper-json-org Json.org
confectory-datamapper-gson Google Gson
confectory-datamapper-jackson2-json Jackson 2
confectory-datamapper-jackson2-toml Jackson 2
confectory-datamapper-jackson2-xml Jackson 2
confectory-datamapper-jackson2-yaml Jackson 2
confectory-datamapper-saxon12 Saxon-HE 12
confectory-datamapper-snakeyaml SnakeYAML

Contributing

If you want to contribute to the Confectory project, check the issues page, or write an e-mail to [email protected].

Confectory uses GitHub Actions for CI/CD.


The Confectory logo and the file-factory animation were created with Inkscape and Natron, both free and open-source Software tools.

confectory's People

Contributors

dependabot[bot] avatar fernandonsc5 avatar jalzeidi avatar oswaldobapvicjr avatar snyk-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

confectory's Issues

New module confectory-datamapper-jackson2-toml

New feature

Proposed solution

Introducing a new module confectory-datamapper-jackson2-toml for mapping TOML sources using Jackson 2 either as Jackson's JsonNode implementation or as a user-defined bean.

The new module shall be dependent on confectory-datamapper-jackson2.

Module Provider(s) Properties XML JSON YAML TOML
confectory-datamapper-jackson2-toml* Jackson 2 + TOML dataformat -- -- YES -- YES

Examples of usage

Loading a TOML file as Jackson's JsonNode

Sample file (university.toml)
[university]
  name = "University of London"
  country = "United Kingdom"
  [university.department]
    name = "Electrical Engineering"
    students = 2750
    subjects = ["Mathematics","Control Systems","Power Systems"]
Code sample (desired):
Configuration config = Configuration.<JsonNode>builder()
                                .source(new ClasspathFileSource<>("university.toml"))
                                .mapper(new JacksonTOMLToJsonNodeMapper())
                                .build();

config.getBean(); // returns a Jackson's JsonNode
config.getString("$.university.country"); // United Kingdom
config.getString("$.university.department.students"); // 2750 

Task list

The following requirements shall be addressed in this issue:

  • Create a new module project confectory-datamapper-jackson2-toml (dependent on confectory-datamapper-jackson2)
  • New concrete Mapper implementation to load TOML sources as Jackson's JsonObjects
  • New concrete Mapper implementation to load TOML sources and user-defined beans)
  • JUnits

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0016)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Strict Configuration

Enhancement/Feature request

Is your feature request related to a problem? Please describe.
No.

Describe the solution you'd like
I'd like a Configuration mode that, once enabled, throws an exception if a JSONPath expression is valid but maps to null (or not found) in the document in scope (not a default value).

Describe alternatives you've considered
Not applicable.

Solution details

TBD

Additional details

  • The code shall be developed in the issue/0047 branch.

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Introducing the confectory-datamapper-jackson2-xml module

New feature

Proposed solution

Introducing a new module confectory-datamapper-jackson2-xml (the name may still be updated) for mapping XML sources using Jackson 2 either as Jackson's JSON Node or as a user-defined bean.

The new module shall be dependent on confectory-datamapper-jackson2.

Module Provider(s) Properties XML JSON YAML
confectory-datamapper-jackson2-xml* Jackson 2 + XML dataformat -- YES YES --

Examples of usage

Loading a XML file as Jackson's Jsonbject

Sample file (agents.xml)
<conf>
   <enabled>true</enabled>
   <agents>
      <agent>
         <class>Agent1</class>
         <interval>*/2 * * * *</interval>
      </agent>
      <agent>
         <class>Agent2</class>
         <interval>10s</interval>
      </agent>
   </agents>
</conf>
Code sample (desired):
Configuration config = Configuration.<JsonNode>builder()
                                .source(new ClasspathFileSource<>("agents.xml"))
                                .mapper(new JacksonXMLToJsonNodeMapper())
                                .build();

config.getBean(); // returns a Jackson's JsonNode
config.getBooleanProperty("$.enabled");        // true
config.getStringProperty("$.agents[0].class"); // "Agent1"

Task list

The following requirements shall be addressed in this issue:

  • Create a new module project confectory-datamapper-jackson2-xml (dependent on confectory-datamapper-jackson2)
  • New concrete Mapper implementation (suggested name: JacksonXMLToJsonNodeMapper) for loading XML sources (as Jackson's JsonNode only (user-defined Java beans shall be addressed by a dedicated issue)
  • JUnits

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0015)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Important exception details omitted on optional configuration

Enhancement

Background/proposed solution

During optional source loading, crucial parsing error information is being suppressed (printed only in debug/trace level), and the user may have a wrong conclusion that the resource was not found. Actually, these data are vital during mapping and should not be suppressed. It is OK to suppress the whole stack trace if the level is "warning", for example. But the root cause message should always be printed in the warning message.

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0057)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Introducing the confectory-datamapper-lite module

New feature

Proposed solution

Introducing a new module confectory-datamapper-lite (the name may still be updated) for mapping JSON sources and converting Properties and XML into JSON.

Module Provider(s) Properties XML JSON YAML
confectory-datamapper-lite* JSON-Java + JayWay JSONPath YES YES YES --

Examples of usage

Loading a JSON file as JSONObject

Sample file (agents.json)
{
  "enabled": true,
  "agents": [
    {
      "class": "Agent1",
      "interval": "*/2 * * * *"
    },
    {
      "class": "Agent2",
      "interval": "10s"
    }
  ]
}
Code sample (desired):
Configuration config = Configuration.<JSONObject>builder()
                                .source(new ClasspathFileSource<>("agents.json"))
                                .mapper(new JSONObjectMapper())
                                .build();

config.getBean(); // returns a JSONObject
config.getBooleanProperty("$.enabled");        // JSONPath -- to be implemented in a dedicated issue
config.getStringProperty("$.agents[0].class"); // JSONPath -- to be implemented in a dedicated issue

NOTE: JSONPath support will be introduced in a dedicated issue

Task list

The following requirements shall be addressed in this issue:

  • Create a new module project confectory-datamapper-lite (dependent on confectory-core)
  • New concrete Mapper implementation (suggested name: JSONObjectMapper) for loading JSON sources (as JSONObjects)
  • New concrete Mapper implementation (suggested name: XMLtoJSONObjectMapper) for loading XML sources as JSONObjects
  • New concrete Mapper implementation (suggested name: PropertiesToJSONObjectMapper) for loading .properties sources as JSONObjects
  • New Reader interface, with a concrete JSONObjectReader implementation (with the getBean() method implemented but the other methods should throw an UnsupportedOperationException for now)

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0012)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

New PropertiesConfigurationMerger

New feature

Proposed solution

Enhancing the confectory-core with a ConfigurationMerger interface and a specialized PropertiesConfigurationMerger.

Module Provider(s) Properties XML JSON YAML
confectory-core Java (standard) YES -- -- --

Examples of usage

Merging two properties files

File 1 (file1.properties)
booleanValue=true
stringValue=string1
intValue=9
File 2 (file2.properties)
booleanValue=true
stringValue=string2
Code sample (desired):
Configuration<Properties> config1 = Configuration.<Properties>builder()
                                .source(new ClasspathFileSource<>("file1.properties"))
                                .mapper(new PropertiesMapper())
                                .precedence(10)
                                .build();

Configuration<Properties> config2 = Configuration.<Properties>builder()
                                .source(new ClasspathFileSource<>("file2.properties"))
                                .mapper(new PropertiesMapper())
                                .precedence(20) // greater precedence than config1
                                .build();

Configuration<Properties> merged = new PropertiesConfigurationMerger()
                                .merge(config1, config2);

merged.getBean(); // returns a Properties object
merged.getBooleanProperty("booleanValue"); // true
merged.getStringProperty("stringValue"); // "string2", as config2 has higher precedence
merged.getIntProperty("intValue"); // 9

Task list

The following requirements shall be addressed in this issue:

  • Create a new ConfigurationMerger interface with a concrete PropertiesConfigurationMerger, which performs merging of two Configuration<Properties> objects.
  • JUnits

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0019)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

CI implementation setup with GitHub Actions

New feature

Proposed solution

This issue aims to implement the required changes to allow build automation for pull requests and collect code metrics.

The following tasks shall be addressed by this issue:

  • New GitHub workflow for building and testing all modules with Maven and Java 8
  • New GitHub workflow for building and testing all modules with Maven and Java 11
  • New GitHub workflow for building and testing all modules with Maven and Java 16 (required until 2021-Sep-14)
  • New GitHub workflow for building and testing all modules with Maven and Java 17-ea
  • Collecting code coverage data using JaCoCo
  • Reporting code coverage metrics on Codecov

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0005)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Jackson mappers to produce user-defined beans

New feature

Proposed solution

Enhancements on Jackson modules for mapping JSON/XML/YAML sources using Jackson 2 either as a user-defined bean.

Examples of usage

Loading a XML file as user-defined bean

Sample file (agents.xml)
<conf>
   <enabled>true</enabled>
   <agents>
      <agent>
         <class>Agent1</class>
         <interval>*/2 * * * *</interval>
      </agent>
      <agent>
         <class>Agent2</class>
         <interval>10s</interval>
      </agent>
   </agents>
</conf>
Sample user-defined classes
public class Agents {
   @JsonProperty
   private boolean enabled;
   @JsonProperty("agents")
   private List<Agent> agents;
   // constructor, getters and setters ommited
}

public class Agent {
   @JsonProperty("class")
   private String clazz;
   @JsonProperty
   private String interval;
   // constructor, getters and setters ommited
}
Code sample (desired):
Configuration config = Configuration.<Agents>builder()
                                .source(new ClasspathFileSource<>("agents.mml"))
                                .mapper(new JacksonXMLToObjectMapper<Agents>())
                                .build();

config.getBean("agents", Agents.class).isEnabled();                   // true
config.getBean("agents", Agents.class).getAgents().get(0).getClazz(); // "Agent1"
config.getBean("agents", Agents.class).getAgents();                   // List<Agent>

Task list

The following requirements shall be addressed in this issue:

  • New concrete Mapper implementations for loading XML/JSON/YAML sources (as user-defined beans)
  • JUnits - JSON
  • JUnits - XML
  • JUnits - YAML

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0038)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Introducing the default/static Configuration Container

New feature

Proposed solution

This issue aims to implement a default/static Configuration container, for basic users who don't mind having a static configuration container. This feature is totally optional for more advanced users who use Dependency Injection frameworks (such as Spring, Guice, etc.).

A new empty Configuration Container shall be loaded automatically and stored in a sort of "façade class" named Confectory.
The default container can be retrieved by calling the static method Confectory.container().

Examples:

Confectory.container().add(new ConfigurationBuilder<Properties>()
                                .namespace("my-props")
                                .source(new ClasspathFileSource<>("my-props.properties"))
                                .mapper(new PropertiesMapper()).build());

Confectory.container().add(new ConfigurationBuilder<Properties>()
                                .namespace("agents")
                                .source(new ClasspathFileSource<>("agents.properties"))
                                .mapper(new PropertiesMapper()).build());

Confectory.container().getBooleanProperty("my-props", "web.enable"); // true
Confectory.container().getBooleanProperty("agents", "web.enable"); // false

The following tasks shall be addressed in the confectory-core module by this issue:

  • New method for obtaining the default container inside the Confectory façade
  • New method for setting a preset container instance as the default inside the Confectory façade
  • New method for resetting the default container inside the Confectory façade
  • JUnit test cases

NOTE: This issue depends on #6.

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0007)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Support custom conversion at `@Property` level

Enhancement proposal

This enhancement proposes support custom/programmer-defines conversion logic to be applied during parsing of files/sources using the @Property annotation.

For example:

public class MyClass {
    @Property("mybool") // use default converter defined by ParseFactory
    private boolean myBoolean;
    @Property(value="myObject", converter=AgentConverter.class) // new
    private Agent myAgent;
}
(...)
public class AgentConverter implements PropertyConverter<Agent> {
    public Agent convert(String string) {
        return Agent.parse(string); // custom conversion
    } 
}

Tasks

  • New interface PropertyConverter<T>
  • Integration with the PropertiesToObjectMapper<T>
  • JUnits
  • Javadocs

New ConfigurationContainer data fetch strategies

New feature

Background

With the introduction of enhancement #6, it's now possible to retrieve data from multiple Configuration objects seamlessly passing a namespace and key.

However, the namespace is optional. So, in a ConfigurationContainer, the user shall retrieve data from Configuration objects without a namespace by using the single-argument getter methods.

Examples:

container.getStringProperty("key1"); // retrieves data from configurations without namespace
container.getStringProperty("namespace1", "key1"); // retrieves data from configurations of namespace "namespace1"

Proposed solution

Introducing two different data fetch strategies inside a ConfigurationContainer to be applied when the single-argument getter methods are called (i.e., without a specific namespace):

  • STRICT: Retrieves data from Configuration objects without namespace only (current behavior; default)
  • LENIENT: Retrieves data from all Configuration objects, i.e., disregard the namespaces (alternative strategy)

NOTES:

  • 1: This setting is totally optional and basic usability shall not be affected by this enhancement.
  • 2: The default data fetch strategy shall be configurable inside the global ConfectoryConfiguration object.

Examples of usage

Code sample (desired):
ConfigurationContainer container1 = new ConfigurationContainer(DataFetchStrategy.STRICT, <configs...>);
ConfigurationContainer container2 = new ConfigurationContainer(DataFetchStrategy.LENIENT, <configs...>);
ConfigurationContainer container3 = new ConfigurationContainer(<configs...>); // uses default configured data fetch strategy

container1.getStringProperty("key1"); // queries configuration without namespace only
container2.getStringProperty("key1"); // queries all configuration objects regardless of namespace

Task list

The following requirements shall be addressed in this issue:

  • Create the DataFetchStrategy enum with the Strict behavior
  • Create the DataFetchStrategy enum with the Lenient behavior
  • Setup the default data fetch strategy configuration at ConfectoryConfiguration
  • Integrate the DataFetchStrategy with the ConfigurationFactory class
  • JUnits

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0024)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Introducing the confectory-datamapper-jackson2-yaml module

New feature

Proposed solution

Introducing a new module confectory-datamapper-jackson2-yaml (the name may still be updated) for mapping YAML sources using Jackson 2 either as Jackson's JsonNode implementation or as a user-defined bean.

The new module shall be dependent on confectory-datamapper-jackson2.

Module Provider(s) Properties XML JSON YAML
confectory-datamapper-jackson2-yaml* Jackson 2 + YAML dataformat -- -- YES YES

Examples of usage

Loading a XML file as Jackson's JsonNode

Sample file (agents.yaml)
enabled: true
agents:
  - class: Agent1
    interval: '*/2 * * * *'
  - class: Agent2
    interval: 10s
Code sample (desired):
Configuration config = Configuration.<JsonNode>builder()
                                .source(new ClasspathFileSource<>("agents.yaml"))
                                .mapper(new JacksonYAMLToJsonNodeMapper())
                                .build();

config.getBean(); // returns a Jackson's JsonNode
config.getBooleanProperty("$.enabled");        // true
config.getStringProperty("$.agents[0].class"); // "Agent1"

Task list

The following requirements shall be addressed in this issue:

  • Create a new module project confectory-datamapper-jackson2-yaml (dependent on confectory-datamapper-jackson2)
  • New concrete Mapper implementation (suggested name: YAMLToJsonNodeMapper) for loading XML sources (as Jackson's JsonObjects and user-defined beans)
  • JUnits

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0016)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

New module confectory-datamapper-snakeyaml

New feature

Proposed solution

Introducing a new module confectory-datamapper-snakeyaml (the name may still be updated) for mapping YAML sources either as JSONObject (minidev's smart-json, which is a hashmap and allows for JSONPath) or as a user-defined bean.

The new module shall be dependent on confectory-datamapper-core.

Module Provider(s) Properties XML JSON YAML
confectory-datamapper-snakeyaml SnakeYAML -- -- ? YES

Examples of usage

Loading a YAML file as JSONObject

Sample file (customer.yaml)
firstName: John
lastName: Doe
age: 31
height: 1.75
contactDetails:
   - type: mobile
     number: 123456789
   - type: landline
     number: 456786868
homeAddress:
   line: 123, ABC Street
   city: City Y
   state: State Y
   zip: 345657
Code sample (desired):
Configuration config = Configuration.<JSONObject>builder() // minidev's smart-json
                                .source(new ClasspathFileSource<>("customer.yaml"))
                                .mapper(new YAMLToJSONObjectMapper())
                                .build();

config.getBean(); // returns a smart JSON object (assignable from HashMap)
config.getString("$.contactDetails[?(@.type=='landline')].number"); // 456787878

Task list

The following requirements shall be addressed in this issue:

  • Create a new module project confectory-datamapper-snakeyaml
  • New concrete Mapper implementation (suggested name: YAMLToJSONObjectMapper) for loading JSON sources as JSON Object
  • New concrete Mapper implementation (suggested name: YAMLToObjectMapper) for loading JSON sources as POJO
  • JUnits

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0017)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Upgrade to Jackson 2.13.1

Describe the bug
Medium severity vulnerability (score 5.9) associated with the current version of jackson-databind (a library that contains the general-purpose data-binding functionality and tree-model for Jackson Data Processor).
The affected versions are vulnerable to Denial of Service (DoS) attack when using JDK serialization to serialize and deserialize JsonNode values. It is possible for the attacker to send a 4-byte length payload, with a value of Integer.MAX_VALUE , that will eventually cause large buffer allocation and out-of-heap memory.

To Reproduce
n/a

Expected behavior
Upgrade to the latest version available (2.13.1, December/2021)

Screenshots
n/a

Additional context
https://security.snyk.io/vuln/SNYK-JAVA-COMFASTERXMLJACKSONCORE-2326698

Support custom type conversion from INI to object

Enhancement

This enhancement proposes support custom/programmer-defines conversion logic to be applied during parsing of files/sources using the @Property annotation on INI sources (following the same logic applied for Property sources in #127(#130).

For example:

public class MyClass {
    @Property("mybool") // use default converter defined by ParseFactory
    private boolean myBoolean;
    @Property(key="myObject", converter=AgentConverter.class) // new
    private Agent myAgent;
}
(...)
public class AgentConverter implements PropertyConverter<Agent> {
    public Agent convert(String string) {
        return Agent.parse(string); // custom conversion
    } 
}

Tasks

  • Integration with INIToObjectMapper<T>
  • JUnits
  • Javadocs

Support Jackson modules discovery in `JacksonJsonToObjectMapper`

Issue description

I have a JSON containing a date object represented as ISO-8601 string:

{
  "id" : "257264a1-adf2-4a02-8417-f3e42e1c7e58",
  "name" : "My Video",
  "publishDate" : "2023-04-26T16:09:10.987654321",
  "numberOfViews" : 1000,
  "hashTags": [ "#java17", "#features" ]
}

And I want to load it into a Record with the following declaration:

public record Video(String id, String name, LocalDateTime publishDate, int numberOfViews, List<String> hashTags) {}

I also have the following dependencies in my pom.xml file:

	<dependencies>
		<dependency>
			<groupId>net.obvj</groupId>
			<artifactId>confectory-datamapper-jackson2-json</artifactId>
			<version>2.3.1</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.datatype</groupId>
			<artifactId>jackson-datatype-jsr310</artifactId>
			<version>2.14.2</version>
		</dependency>
	</dependencies>

I'm trying to load the JSON into a Video using the following code:

    private static Video parseVideo(String json)
    {
        return Configuration.<Video>builder()
                .source(new StringSource<>(json))
                .mapper(new JacksonJsonToObjectMapper<>(Video.class))
                .build().getBean();
    }

However, the following exception is raised:

Exception in thread "main" net.obvj.confectory.ConfigurationSourceException: Unable to load string: "{
  "id" : "257264a1-adf2-4a02-8417-f3e42e1c7e58",
  "name" : "My Video",
  "publishDate" : "2023-04-26T16:09:10.987654321",
  "numberOfViews" : 1000,
  "hashTags": [ "#java17", "#features" ]
}
"
	at net.obvj.confectory.source.StringSource.load(StringSource.java:64)
	at net.obvj.confectory.source.AbstractSource.load(AbstractSource.java:73)
	at net.obvj.confectory.ConfigurationService.<init>(Configuration.java:380)
	at net.obvj.confectory.Configuration.getService(Configuration.java:349)
	at net.obvj.confectory.Configuration.<init>(Configuration.java:113)
	at net.obvj.confectory.ConfigurationBuilder.build(ConfigurationBuilder.java:252)
	at VideoTestDrive.parseVideo(VideoTestDrive.java:83)
	at VideoTestDrive.main(VideoTestDrive.java:51)
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
 at [Source: (ByteArrayInputStream); line: 4, column: 19] (through reference chain: Video["publishDate"])
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
	at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1909)
	at com.fasterxml.jackson.databind.deser.impl.UnsupportedTypeDeserializer.deserialize(UnsupportedTypeDeserializer.java:48)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:542)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:564)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:439)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1405)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:352)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4730)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3714)
	at net.obvj.confectory.mapper.JacksonJsonToObjectMapper.apply(JacksonJsonToObjectMapper.java:60)
	at net.obvj.confectory.source.AbstractSource.load(AbstractSource.java:65)
	at net.obvj.confectory.source.StringSource.load(StringSource.java:60)
	... 7 more

Expected behavior
The JacksonJsonToObjectMapper should configure Jackson's ObjectMapper in a way that allows it to detect the module com.fasterxml.jackson.datatype:jackson-datatype-jsr310, which is available in the pom.xml file, so Jackson should use it to perform the conversion from String to LocalDateTime.

Additional context
According to this discussion (https://stackoverflow.com/questions/27952472/serialize-deserialize-java-8-java-time-with-jackson-json-mapper) the issue could be solved by adding the following instruction objectMapper.findAndRegisterModules() during mapping.

During some tests, the following piece of code worked fine in OpenJDK 17:

ObjectMapper mapper = new ObjectMapper();
mapper.findAndRegisterModules();
return mapper.readValue(json, Video.class);

Tasks

  • JacksonJsonNodeMapper - Adaptations
  • JacksonJsonNodeMapper - JUnit
  • JacksonJsonToObjecMapper - Adaptations
  • JacksonJsonToObjecMapper - JUnit
  • JacksonXMLToJsonNodeMapper - Adaptations
  • JacksonXMLToJsonNodeMapper - JUnit
  • JacksonXMLToObjectMapper - Adaptations
  • JacksonXMLToObjectMapper - JUnit
  • JacksonTOMLToJsonNodeMapper - Adaptations
  • JacksonTOMLToJsonNodeMapper - JUnit
  • JacksonTOMLToObjectMapper - Adaptations
  • JacksonTOMLToObjectMapper - JUnit
  • JacksonYAMLToJsonNodeMapper - Adaptations
  • JacksonYAMLToJsonNodeMapper - JUnit
  • JacksonYAMLToObjectMapper - Adaptations
  • JacksonYAMLToObjectMapper - JUnit
  • Documentation/Wiki

Introducing the ConfigurationContainer

New feature

Proposed solution

This issue aims to implement the configuration container, an object intended to hold multiple Configuration objects and retrieve configuration data seamlessly, by namespace and key.

In case of key collision in the same namespace, the configuration with the highest precedence value shall be selected.

Examples:

Configuration config1 = Configuration.<Properties>builder()
                                .namespace("my-props")
                                .source(new ClasspathFileSource<>("my-props.properties"))
                                .mapper(new PropertiesMapper())
                                .build();

Configuration config2 = Configuration.<Properties>builder()
                                .namespace("agents")
                                .source(new ClasspathFileSource<>("agents.properties"))
                                .mapper(new PropertiesMapper())
                                .build();

ConfigurationContainer container = new ConfigurationContainer(config1, config2);

container.getBooleanProperty("my-props", "web.enable"); // true
container.getBooleanProperty("agents", "web.enable"); // false

The following tasks shall be addressed in the confectory-core module by this issue:

  • New interface ConfigurationData to be used as a parent for the existing Configuration and ConfigurationHelper classes. Configuration-getter methods shall be defined in this new interface
  • New ConfigurationContainer class, implementing the newly created ConfigurationData interface
  • New interface ConfigurationMetadata to be used as a parent for the existing Configuration and ConfigurationBuilder classes. Configuration-metadata getters (such as getSource(), getMapper(), etc.) shall be defined in this new interface
  • JUnit test cases

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0006)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

New method Configuration.merge(Configuration other)

Is your feature request related to a problem? Please describe.

No

Describe the solution you'd like

Add a new merge method into the existing Configuration class, so that users may perform configuration merge without understanding the framework's internals.

The new method may delegate to the one of the recently introduced ConfigurationMerger. The available ConfigurationHelper can be used to instantiate the correct merger object for the current Configuration setup:

  • PropertiesConfigurationMerger
  • JSONObjectConfigurationMerger
  • JsonOrgConfigurationMerger
  • GsonJsonObjectConfigurationMerger
  • JacksonJsonNodeConfigurationMerger
  • JUnit tests

Note: Advanced parameters currently available for JSON merging (distinct keys) shall be supported only in a future phase.

Describe alternatives you've considered

Advanced users may still consider using the ConfigurationMerger classes for fine settings.

Additional context

N/A

Introducing the ConfigurationBuilder with support to Properties file

New feature

Proposed solution

Introducing the new project setup with capabilities to handle local properties files from the classpath, according to the example:

Configuration config = Configuration.<Properties>builder()
                                .source(new ClasspathFileSource<>("my-props.properties"))
                                .mapper(new PropertiesMapper())
                                .build();

config.getBooleanProperty("web.enable"); // true
config.getStringProperty("web.host");    // "localhost"
config.getIntProperty("web.port");       // 1910

The following requirements shall be addressed in this issue:

  • Add a new parent POM confectory-base
  • Add a new module confectory-core
  • New Source interface, with a concrete ClasspathFileSource implementation for local classpath resources
  • New concrete Source implementation FileSource with built-in environment variable replacement
  • New Mapper interface, with a concrete PropertiesMapper implementation for loading Properties files
  • New Reader interface, with a concrete PropertiesReader implementation
  • New Configuration and ConfigurationBuilder objects with capabilities to handle properties files
  • Add logs using Log4j2

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0002)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Supporting optional configuration/properties

New feature

Proposed solution

This issue aims to implement the required changes to allow for optional configuration properties.

If a Configuration is marked as optional, it means that no exception is thrown if the Source is not available, but this doesn't explicitly describe what should happen when a Configuration is loaded successfully but with some specific properties/keys being optional.

Currently, when a key is not found, the system throws a ConfigurationException informing that the requested key is not found.
However, considering that the Confectory project is designed to work seamlessly with multiple Sources, it's preferable to consider all properties/keys as optional by default.

With this improvement, the Configuration and its associated helpers shall avoid the exception when a key is not found. Instead, the system shall return "smart nulls".

For primitive types, the system shall rely on extensible "null-value providers", which could be implemented by advanced users and a default object (that returns basic "smart nulls") shall be provided so that basic users can use them OOB, i.e., without any additional effort.

Examples:

Configuration config = Configuration.<Properties>builder()
                                .source(new ClasspathFileSource<>("my-props.properties"))
                                .mapper(new PropertiesMapper())
                                .nullValueProvider(new NullValueProvider<>() { // optional
                                     //implementation ommited
                                 })
                                .build();

config.getBooleanProperty("web.enable"); // not found... delegates to NullValueProvider

The following tasks shall be addressed in the confectory-core module by this issue:

  • New NullValueProvider interface with a concrete StandardNullValueProvider, which returns "small-nulls" ("" for Strings, 0 for numbers and false for booleans)
  • New static facility to allow setting the default NullValueProvider only once, if required
  • Enhance the ConfigurationHelper interface as well as existing implementation(s) with new optional methods (getOptionalBoolean(String), getOptionalString(String), etc.)
  • JUnit test cases (new feature)
  • JUnit test cases (remaining untested features of #2)

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0004)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Introducing the confectory-datamapper-gson2 module

New feature

Proposed solution

Introducing a new module confectory-datamapper-gson2 (the name may still be updated) for mapping JSON sources either as GSON's default JSON object implementation or as a user-defined bean.

The new module shall be dependent on confectory-datamapper-core (this condition may be updated in the future).

Module Provider(s) Properties XML JSON YAML
confectory-datamapper-gson* GSON -- -- YES --

Examples of usage

Loading a JSON file as GSON's JSONObject

Sample file (agents.json)
{
  "enabled": true,
  "agents": [
    {
      "class": "Agent1",
      "interval": "*/2 * * * *"
    },
    {
      "class": "Agent2",
      "interval": "10s"
    }
  ]
}
Code sample (desired):
Configuration config = Configuration.<JSONObject>builder() // ???
                                .source(new ClasspathFileSource<>("agents.json"))
                                .mapper(new GsonMapper())
                                .build();

config.getBean(); // returns a GSON's JSON object
config.getBooleanProperty("$.enabled");        // true
config.getStringProperty("$.agents[0].class"); // "Agent1"

Task list

The following requirements shall be addressed in this issue:

  • Create a new module project confectory-datamapper-gson2
  • New concrete Mapper implementation (suggested name: GsonMapper) for loading JSON sources as GSON's JSON Object
  • New concrete Mapper implementation (suggested name: GsonToObjectMapper) for loading JSON sources as POJO
  • JUnits

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0017)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Lazy Configuration

Enhancement/Feature request

Is your feature request related to a problem? Please describe.
No.

Describe the solution you'd like
I'd like a Configuration option (lazy) that could be accepted by the ConfigurationBuilder which prevents the Source from being actually loaded until a getter is called for it.

Describe alternatives you've considered
Not applicable.

Solution details

TBD

Additional details

  • The code shall be developed in the issue/0048 branch.

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

DynamicSource must differentiate a mapping error from a file-not-found

Improvement

Background

During troubleshooting with the DynamicSource, we realized that the class is always falling back to the File Source, regardless of the nature of the failure with the Classpath Source, even in situations where the trouble was in the mapping part, that is, the file does exist in the Classpath. In this case, the real exception was suppressed and the system tried (uselessly) to search the file system. The error reported by the second Source is actually related to file-not-found (correctly, because the resource exists in the classpath).

Proposed solution

In cases where the file is found but an error is raised during the mapping, the DynamicSource shall not try other sources anymore. The actual exception shall be propagated so the API user can have information to fix the mapping.

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0040)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

New ConfigurationMergerFactory

New feature

Proposed solution

This issue is intended to introduce a ConfigurationMergerFactory in confectory-core so that the system may provide a concrete ConfigurationMerger instance given an input type/class.

This factory must be dynamic so that the merger implementations introduced by optional modules could be created too.

Examples of usage

Code sample (desired):
ConfigurationMerger<Properties> = ConfigurationMergerFactory
        .newConfigurationMerger(Properties.class); // Returns a PropertiesConfigurationMerger

ConfigurationMerger<JSONObject> = ConfigurationMergerFactory
        .newConfigurationMerger(JSONObject.class); // Returns a JSONObjectConfigurationMerger

Then, for even better usability, the system could provide (feasibility study still required):

ConfigurationMerger<Properties> = new ConfigurationMerger<Properties>(config1, config2); // PropertiesConfigurationMerger
ConfigurationMerger<JSONObject> = new ConfigurationMerger<JSONObject>(config1, config2); // JSONObjectConfigurationMerger

Task list

The following requirements shall be addressed in this issue:

  • Create the ConfigurationMergerFactory
  • Create the generic ConfigurationMerger<T>(Configuraiton<T> ... configs) constructor (feasibility study required)
  • JUnits

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0021)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Avoid configuration data output via Configuration.toString()

Describe the bug/vulnerability
When the StringSource is used, the actual data is visible via Configuration's toString() method, which is not desirable.
Note: This does not happen with other sources, such as URL or File, in which the system displays the path to the source, instead.

To Reproduce

  1. Create a new Configuration using the StringSource containing a valid JSON document or properties file
  2. Print the Configuration to the system console

Expected behavior
The system should not print the actual data, which can contain sensitive information that might be accessed without proper tracing. Instead, the system could print some "dummy"/random path (similar to the approach used by the Configuration objects generated by the mergers).

Screenshots
Example of toString result generated by string source (current behavior, unexpected):
{"namespace":"test","precedence":0,"source":"StringSource(myFileName=config1\n myString=cust1)"}

Example of toString result generated by merger (desirable also for string source):
{"namespace":"test","precedence":20,"source":"DummySource(7728eee7-a79a-42ed-aec8-1d83b39ed7b2)"}

Desktop:

  • OS: Windows 11
  • Browser: N/A
  • Version: 2.1.0

Additional context
N/A

Measure loading time for file sources

New feature

Proposed solution

This issue aims to measure and report the elapsed time loading files/resources with the Performetrics tool.

The following tasks shall be addressed in the confectory-core module by this issue:

  • Add Performetrics dependency at the project POM file
  • Log the elapsed wall-clock time for the ClasspathFileSource loader
  • Log the elapsed wall-clock time for the FileSource loader

NOTE: This issue depends solely on #2.

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0008)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

New TypeSafeConfigurationContainer<T>

A public way to get the highest-precedence configuration from a list of Configurations of the same type (a sort of TypeSafeConfigurationContainer<T>)

[confectory-core] New JSONObjectConfigurationMerger

New feature

Proposed solution

Enhancing the confectory-core with a new specialized ConfigurationMerger for JSON Objects (using json-smart provider).

Examples of usage

Merging two JSON files

File 1 (file1.json)
{
  "agents": [
    {
      "class": "Agent1",
      "interval": "*/2 * * * *"
    },
    {
      "class": "Agent2",
      "interval": "90s"
    }
  ]
}
File 2 (file2.json)
{
  "enabled": true,
  "agents": [
    {
      "class": "Agent2",
      "interval": "10s"
    }
  ]
}
Code sample (desired):
Configuration<JSONObject> config1 = Configuration.<JSONObject>builder()
                                .source(new ClasspathFileSource<>("file1.json"))
                                .mapper(new JSONObjectMapper())
                                .precedence(9) // higher precedence
                                .build();

Configuration<JSONObject> config2 = Configuration.<JSONObject>builder()
                                .source(new ClasspathFileSource<>("file2.json"))
                                .mapper(new JSONObjectMapper())
                                .precedence(1) // lower precedence
                                .build();

Configuration<JSONObject> merged = new JSONObjectConfigurationMerger()
                                .withMergeOptions( // accept varargs
                                     MergeOption.distinctObjectsIdentifiedByAttribute("class")
                                         .insideArray("$['agents']") // jsonpath
                                .merge(config1, config2);

merged.getBean(); // returns a JSONObject
merged.getBooleanProperty("$.enabled"); // true (from config2)
merged.getStringProperty("$.*[?(@.class=='Agent1')].interval"); // "*/2 * * * *", as from config1
merged.getStringProperty("$.*[?(@.class=='Agent2')].interval"); // "90s", as config1 has higher precedence

Some comments on the withDistinctSet() method:

  • By default, all root elements on the JSON object could be considered distinct/unique, but objects in any other level in the JSON tree (e.g. inside an array or other objects) which should be considered distinct/unique (such as the agent class attribute, in the example above) should be informed to the merger.
  • The merger shall NOT rely on the position of elements inside an array to do the merge

Task list

The following requirements shall be addressed in this issue:

  • Create a new concrete JSONObjectConfigurationMerger, which performs merging of two Configuration<JSONObject> objects.
  • Implement custom merging of JSONObjects inside JSONArray, based on distinct object keys for conflict handling
  • Implement the MergeOption class, with fluent API for declaring distinct object keys inside an array
  • JUnits

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0020)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Retrieving JSON configuration data with JSONPath

New feature

Proposed solution

This issue is dedicated to adding JSONPath support to confectory-datamapper-core so that configuration data from JSON objects (as well as converted XML and Properties sources) can be retrieved using JSON Path.

Examples of usage

Loading a JSON file as JSONObject

Sample file (agents.json)
{
  "enabled": true,
  "agents": [
    {
      "class": "Agent1",
      "interval": "*/2 * * * *"
    },
    {
      "class": "Agent2",
      "interval": "10s"
    }
  ]
}
Code sample (desired):
Configuration config = Configuration.<JSONObject>builder()
                                .source(new ClasspathFileSource<>("agents.json"))
                                .mapper(new JSONObjectMapper())
                                .build();

config.getBean(); // returns a JSONObject
config.getBooleanProperty("$.enabled");        // true
config.getStringProperty("$.agents[0].class"); // "Agent1"

NOTE: This issue depends on #12.

Task list

The following requirements shall be addressed in this issue:

  • Add JayWay JSONPath dependency inside confectory-core POM file.
  • Add new methods in the ConfigurationHelper interface for retrieving objects from a key/path
    • T getObject(String) which reads a JSONPath and returns a generic object (e.g.: JSONObject for ConfigurationHelper<JSONObject>) from the JSON
    • List<T> getList(String) which reads a JSONPath and returns an ArrayList of generic objects (e.g.: ArrayList<JSONObject> for ConfigurationHelper<JSONObject>) from the JSON
    • Add default (no-op) implementations for existing ConfigurationHelpers introduced in 0.1.0
    • Add a new concrete JSONObjectConfigurationHelper implementation which evaluates JSON paths and retrieve data
  • Add new JUnits
  • Update JUnits (modified helpers)

IMPORTANT: Avoid using static JSONPath configuration to allow multiple JSON providers planned on future releases

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0013)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

ConfigurationContainer API improvements

Is your feature request related to a problem? Please describe.
No.

Describe the solution you'd like
General API improvements on the ConfigurationContainer:

  • public long size()
  • public boolean isEmpty()
  • public boolean isEmpty(String namespace)
  • #54

Describe alternatives you've considered
n/a

Additional context
n/a

Allow object instantiation without calling the default constructor

Is your feature request related to a problem? Please describe.
Partially. I'm always frustrated when I have to write an empty default constructor to allow deserialization of Properties and INI files, while JSON frameworks such as Gson and Jackson allow the same without requiring a default constructor.

Describe the solution you'd like
A solution based on sun.misc.Unsafe so that an empty instance is created for deserialization, without calling any constructor, as described in https://dzone.com/articles/understanding-sunmiscunsafe.

Ideally, the programmer could disable this behavior via settings and use the "classic" solution, based on Reflection, if they prefer it.

Additional context

https://www.baeldung.com/java-unsafe

Introducing the URL Source

New feature

Proposed solution

This issue is dedicated to introducing a new Source implementation that loads content from a REST/Web Service using Java standard objects (generic, no specific HTTP requirement so far).

  • The new Source shall be used in combination with any of the existing Mappers (XML, JSON, properties, YAML, etc.)
  • The new Source shall be implemented in the confectory-core module.

Examples of usage

Loading a JSON file as JSONObject

Sample content (loaded from the Web)
{
  "enabled": true,
  "agents": [
    {
      "class": "Agent1",
      "interval": "*/2 * * * *"
    },
    {
      "class": "Agent2",
      "interval": "10s"
    }
  ]
}
Code sample (desired):
Configuration config = Configuration.<JSONObject>builder()
                                .source(new URLSource<>("http://myhost:8081/agents"))
                                .mapper(new JSONObjectMapper()) // from confectory-datamapper-lite
                                .build();

config.getBean(); // returns a JSONObject
config.getBooleanProperty("$.enabled");        // true
config.getStringProperty("$.agents[0].class"); // "Agent1"

Task list

The following requirements shall be addressed in this issue:

  • New concrete Source implementation URLSource using JDK (8) standard functionality
  • JUnits and E2E tests

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0018)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Introducing the confectory-datamapper-jackson2 module

New feature

Proposed solution

Introducing a new module confectory-datamapper-jackson2 (the name may still be updated) for mapping JSON sources using Jackson 2 either as Jackson's JsonNode implementation or as a user-defined bean.

Module Provider(s) Properties XML JSON YAML
confectory-datamapper-jackson2* Jackson 2 -- -- YES --

Examples of usage

Loading a JSON file as Jackson's JsonNode

Sample file (agents.json)
{
  "enabled": true,
  "agents": [
    {
      "class": "Agent1",
      "interval": "*/2 * * * *"
    },
    {
      "class": "Agent2",
      "interval": "10s"
    }
  ]
}
Code sample (desired):
Configuration config = Configuration.<JsonNode>builder()
                                .source(new ClasspathFileSource<>("agents.json"))
                                .mapper(new JacksonJsonNodeMapper())
                                .build();

config.getBean(); // returns a Jackson's JsonNode
config.getBooleanProperty("$.enabled");        // true
config.getStringProperty("$.agents[0].class"); // "Agent1"

Task list

The following requirements shall be addressed in this issue:

  • Create a new module project confectory-datamapper-jackson2 (dependent on confectory-core)
  • New concrete Mapper implementation (suggested name: JacksonJsonNodeMapper) for loading JSON sources (as Jackson's JsonNode)
  • New Reader interface, with a concrete JacksonJsonNodeReader implementation (all methods implemented)
  • Do the applicable refactorings to avoid duplicated coding
  • JUnits

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0014)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Preparing for release 0.1.0

New feature

Proposed solution

This issue is intended to address minor improvements before the release of Confectory 0.1.0.

Task list

The following requirements shall be addressed in this issue:

  • Add Javadoc overview page
  • Add package-level Javadocs
  • Review class Javadocs (fix typos/add relevant examples)
  • Setup custom CSS for gerenated Javadoc pages
  • Fix typos in POM.xml file/review POM structure

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0029)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

Lower-precedence boolean value returned by ConfigurationContainer

Describe the bug
ConfigurationContainer returns lower-precedence boolean value when property evaluates to false in the highest-precedence JSON.

To Reproduce
Steps to reproduce the behavior:

  1. Place a file named conf_high.yaml in the classpath with the following content:

    enabled: false
  2. Place a file named conf_low.yaml in the classpath with the following content:

    enabled: true
  3. Load the sources with the following code:

    	Configuration<JsonNode> config1 = Configuration.<JsonNode>builder()
    			.source("classpath://conf_high.yaml")
    			.mapper(new JacksonYAMLToJsonNodeMapper())
    			.precedence(10)
    			.build();
    	Configuration<JsonNode> config2 = Configuration.<JsonNode>builder()
    			.source("classpath://conf_low.yaml")
    			.mapper(new JacksonYAMLToJsonNodeMapper())
    			.precedence(5)
    			.build();
    	ConfigurationContainer config = new ConfigurationContainer(config1, config2);
    	System.out.println(config.getBoolean("agents", "$.enabled") + ""); 

Expected behavior
ConfiurationContainer should have returned false because the property exists in the YAML source.

Screenshots
n/a

Additional context
n/a

New DynamicSource

New feature

Proposed solution

This enhancement introduces a new Source implementation that returns either of the existing file source implementations depending on the path specified by the user.

Examples of usage

Code sample (desired):
Configuration<Properties> config1 = Configuration.<Properties>builder()
             // .source(new ClasspathFileSource("testfiles/my-props.properties")) // <- exising method
                .source("classpath://testfiles/my-props.properties") // <- NEW -- DynamicSource (calls ClasspathFileSource internally)
                .mapper(new PropertiesMapper()).build();

Configuration<Properties> config2 = Configuration.<Properties>builder()
             // .source(new FileSource("/tmp/my-props.properties")) // <- exising method
                .source("file://tmp/my-props.properties") // <- NEW -- DynamicSource (calls FileSource internally)
                .mapper(new PropertiesMapper()).build();

Configuration<Properties> config3 = Configuration.<Properties>builder()
                .source("http://myhost:1910/my-props") // <- DynamicSource (will call HttpSource in the future)
                .mapper(new PropertiesMapper()).build();

Notes

  • The existing ConfigurationBuilder.source(Source) method may still be used, so it should NOT be replaced
  • The DynamicSource shall be tolerant to user-input errors
  • If unable to determine the source based on the path, ClasspathSource shall be used as a fallback

Task list

The following requirements shall be addressed in this issue:

  • Create a new Source implementation called DynamicSource inside confectory-core
  • Integrate the DynamicSource with the ConfigurationBuilder
  • JUnits

Additional details

  • The code shall be developed in a dedicated branch, with the prefix issue/ followed by the issue number (e.g.: issue/0025)

Quality criteria

The following criteria will be observed during the Code Review:

Code coverage

  • The produced code shall be secured by unit tests.
  • Test coverage shall remain the same or increase.

Documentation

  • All methods shall be documented with Javadoc, providing examples of usage.
  • Javadoc pages shall be compilable with no errors or warnings
  • README.md shall be updated if applicable

JSONPath operation not supported for JsonNode generated by JacksonXMLToJsonNodeMapper

Describe the bug
A ConfigurationException with message "Operation not supported for bean of type 'com.fasterxml.jackson.databind.node.ObjectNode'" is raised trying to extract a JSONPath out of a Configuration with the JacksonXMLToJsonNodeMapper.

To Reproduce
Steps to reproduce the behavior:

  1. Execute the following code:
        Configuration<JsonNode> config = Configuration.<JsonNode>builder()
                .source(SourceFactory.stringSource("<root><foo attrib=\"attribValue\">bar</foo></root>"))
                .mapper(new JacksonXMLToJsonNodeMapper())
                .build();

        System.out.println(config.getString("$.foo.attrib"));
  1. See the error
Exception in thread "main" net.obvj.confectory.ConfigurationException: Operation not supported for bean of type 'com.fasterxml.jackson.databind.node.ObjectNode'
	at net.obvj.confectory.helper.BeanConfigurationHelper.getString(BeanConfigurationHelper.java:50)
	at net.obvj.confectory.helper.BasicConfigurationHelper.getBoolean(BasicConfigurationHelper.java:48)
	at net.obvj.confectory.Configuration.getBoolean(Configuration.java:180)
	at net.obvj.confectory.testdrive.ConfectoryTestDriveJacksonXMLToJsonNode.main(ConfectoryTestDriveJacksonXMLToJsonNode.java:22)

Expected behavior
The code should have printed attribValue in the console

Additional context
Using confectory-datamapper-jackson2-xml version 0.3.0.

Support additional standard types in `TypeFactory`

This enhancement proposes adding support for standard conversion for the following types from the Java packages:

  • Class
  • BigInteger/BigDecimal
  • Currency (ISO 4217)
  • TimeZone
  • UUID (RFC 4122)
  • InetAddress
  • URI/URL (RFC 2396)
  • File
  • Path
  • Charset
  • java.sql.Time
  • java.time.LocalTime
  • java.time.OffsetTime
  • java.time.ZoneOffset/ZoneId

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.