{"id":18446562,"url":"https://github.com/esastack/esa-commons","last_synced_at":"2025-04-08T00:31:55.392Z","repository":{"id":39715998,"uuid":"317093800","full_name":"esastack/esa-commons","owner":"esastack","description":"Common lib of ESA Stack.","archived":false,"fork":false,"pushed_at":"2022-07-29T08:26:10.000Z","size":438,"stargazers_count":53,"open_issues_count":1,"forks_count":19,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-23T03:11:36.777Z","etag":null,"topics":["custom-logging","lambda","lambda-interfaces","logging","spi-enhancement","unsafeutils","utils"],"latest_commit_sha":null,"homepage":"https://www.esastack.io","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/esastack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-11-30T02:50:09.000Z","updated_at":"2024-11-27T04:00:08.000Z","dependencies_parsed_at":"2022-09-26T21:40:55.337Z","dependency_job_id":null,"html_url":"https://github.com/esastack/esa-commons","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esastack%2Fesa-commons","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esastack%2Fesa-commons/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esastack%2Fesa-commons/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esastack%2Fesa-commons/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/esastack","download_url":"https://codeload.github.com/esastack/esa-commons/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247755389,"owners_count":20990616,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["custom-logging","lambda","lambda-interfaces","logging","spi-enhancement","unsafeutils","utils"],"created_at":"2024-11-06T07:09:41.733Z","updated_at":"2025-04-08T00:31:53.544Z","avatar_url":"https://github.com/esastack.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ESA Commons\n\n![Build](https://github.com/esastack/esa-commons/workflows/Build/badge.svg?branch=main)\n[![codecov](https://codecov.io/gh/esastack/esa-commons/branch/main/graph/badge.svg?token=HUHT6S30PD)](https://codecov.io/gh/esastack/esa-commons)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.esastack/commons/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.esastack/commons/)\n[![GitHub license](https://img.shields.io/github/license/esastack/esa-commons)](https://github.com/esastack/esa-commons/blob/main/LICENSE)\n\n\nESA Commons is the common lib of `ESA Stack`.\n\n## Features\n\n- `SPI` Enhancement:  Allows loading `SPI` by name, group, tag and so on..\n- `Logger`: Detect the `slf4j` automatically.\n- `InternalLogger`: Write log  without any log library.\n- Lambda Enhancement: lambda interfaces receiving multiple parameters,  lambda interfaces allows to throw an exception.\n- `MultiValueMap`: Implementation of `Map\u003cK, List\u003cV\u003e`.\n- `UnsafeUtils`, `UnsafeArrayUtils`: Unity classes for unsafe options.\n- Reflection support: `AnnotationUtils`, `ClassUtils`...\n- more features...\n\n## Maven Dependency\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.esastack\u003c/groupId\u003e\n    \u003cartifactId\u003ecommons\u003c/artifactId\u003e\n    \u003cversion\u003e${mvn.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Reference Guide\n\n### `Logger` \u0026 `LoggerFactory`\n\nThere are many excellent logging frameworks such as log4j, log4j2, logback and so on... Although the slf4j framework serves as a simple facade or abstraction for various logging frameworks, maybe you still do not want to use slf4j as the facade in your own framework which will be used by the end user.  So we provides a couple of  APIs( `Logger`, `LoggerFactory` ) to serve as a facade of slf4j and JDK logger(may be we will support log4j and logback in the future).\n\neg.\n\njust declare the logger as usual\n\n```java\nprivate static final Logger logger = LoggerFactory.getLogger(\"foo\");\n```\n\nalso use it as usual\n\n```java\nlogger.info(\"hell world!\");\nlogger.debug(\"hello {}!\", \"world\");\nlogger.warn(\"{} {}!\", \"hello\", \"world\");\nlogger.error(\"oops!\", new Exception(\"there's something wrong with it\"));\n```\n\n\u003e Please do not forget that we are using the `Logger` and `LoggerFactory` that are provided in `esa-commons` instead of the org.slf4j.Logger.\n\n### `InternalLogger`\n\n`InternalLogger` allows you to write logs without any thirdparty log dependency such as log4j, logback.\n\n\u003e In some thirdparty log frameworks， you may need a xxx.xml to configure your logs to the target file which depends on your end user.\n\neg.\n\n```java\nfinal InternalLogger logger = InternalLoggers.logger(\"foo\", new File(\"foo.log\")).build();\nlogger.info(\"hello world!\");\n```\n\nand you will see the appended log in the file named foo.log\n\n```\n2020-12-10 11:42:10.141 INFO [main] foo : hello world!\n```\n\n#### log pattern\n\nyou can configure the log patterns below\n\n- `%d`, `%date`: log date \n- `%l`, `%level`：log level\n- `%t`, `%thread`: caller thread\n- `%logger`: logger name\n- `%m`, `%msg`, `%message`: log content\n- `%n`: new line\n- `%ex`, `%exception`, `%thrown`:  error detail if present\n\neg.\n\nthe default log pattern: `%date %level [%thread] %logger : %msg%n%thrown`\n\nand the output of `logger.info(\"hello world!\")`\n\n```\n2020-12-10 11:42:10.141 INFO [main] foo : hello world!\n```\n\neg.\n\nlog pattern: `%msg%n`\n\nand the output of \n\n```java\nlogger.info(\"hello\");\nlogger.info(\"world\");\n```\n\nis\n\n```\nhello\nworld\n```\n\n#### File Rolling\n\n- `SIZE_BASED` rolling\n- `TIME_BASED` rolling\n- `TIME_AND_SIZE_BASED` rolling\n\neg.\n\n`SIZE_BASED` rolling with max size of 10M, and the max history log file's number of 3\n\n```java\nfinal long maxSize = 10 * 1024 * 1024;\nfinal int maxHistory = 3;\nfinal InternalLogger logger = InternalLoggers.logger(\"foo\", new File(\"foo.log\"))\n         .useSizeBasedRolling(maxSize, maxHistory)\n         .build();\n```\n\n`TIME_BASED` rolling everyday \n\n```java\nfinal InternalLogger logger = InternalLoggers.logger(\"foo\", new File(\"foo.log\"))\n         .useTimeBasedRolling(\"yyyy-MM-dd\")\n         .build();\n```\n\n`TIME_BASED` rolling every hour\n\n```java\nfinal InternalLogger logger = InternalLoggers.logger(\"foo\", new File(\"foo.log\"))\n         .useTimeBasedRolling(\"yyyy-MM-dd_HH\")\n         .build();\n```\n\n\u003e patter of date only support: Hour and Day level\n\n`TIME_AND_SIZE_BASED` rolling\n\n```java\nfinal long maxSize = 10 * 1024 * 1024;\nfinal int maxHistory = 3;\nfinal InternalLogger logger = InternalLoggers.logger(\"foo\", new File(\"foo.log\"))\n         .useTimeAndSizeBasedRolling(\"yyyy-MM-dd\", maxSize, maxHistory)\n         .build();\n```\n\n### Lambda Enhancement\n\n#### Arguments Enhancement\n\nJDK only supports a part of lambda interfaces, and here we declare more interfaces like\n\n- `Consumer3`, `Consumer4`,  `Consumer5`: `Consumer` with 3(4 or5) arguments\n- `Function3`, `Function4`,  `Function5`: `Function` with 3(4 or5) arguments\n- `Predicate3`, `Predicate4`,  `Predicate5`: `Predicate` with 3(4 or5) arguments\n- and so on...\n\neg.\n\n```java\nConsumer3\u003cInteger, String, Foo\u003e c = (num, str, foo) -\u003e {};\n```\n\n#### Throwing Enhancement\n\nJDK lambda interface is not allowed to throw an exception, and here we declare the interfaces like\n\n- `ThrowingConsumer`, `ThrowingBiConsumer`, `ThrowingConsumer3`, `ThrowingConsumer4`\n\n- `ThrowingFunction`, `ThrowingBiFunction`, `ThrowingFunction3`, `ThrowingFunction4`\n- `ThrowingPredicate`, `ThrowingBiPredicate`, `ThrowingPredicate3`, `ThrowingPredicate4`\n- `ThrowingSupplier`\n- `ThrowingRunnable`\n- and so on...\n\neg.\n\n```java\nThrowingConsumer\u003cString\u003e c = str -\u003e {\n    mayThrowIOException();\n    // ...\n}\n\nstatic void mayThrowIOException() {\n    if (condition) {\n        throw new IOException();\n    }\n}\n```\n\nthis will transfer your `ThrowingConsumer` to `Consumer` which will rethrow the exception(if present).\n\n```java\nConsumer\u003cString\u003e c = ThrowingConsumer.rethrow(str -\u003e {\n    mayThrowIOException();\n    // ...\n});\n```\n\nthis will just suppress the exception\n\n```java\nConsumer\u003cString\u003e c = ThrowingConsumer.suppress(str -\u003e {\n    mayThrowIOException();\n    // ...\n});\n```\n\nthis will handle the exception\n\n```java\nFunction\u003cString, String\u003e c = ThrowingFunction.failover(str -\u003e {\n    mayThrowIOException();\n    return \"foo\";\n}, (v, t) -\u003e {\n    // handle error 't'\n    return \"bar\";\n});\n```\n\n#### Auto-(Un)Box Enhancement\n\nAllows you to use lambda interfaces without Auto-Box or Auto-Unbox.\n\n- `ObjIntFunction`, `ObjDoubleFunction`, `ObjLongFunction`\n- `ObjIntPredicate`, `ObjDoublePredicate`, `ObjLongPredicate`\n- `ThrowingIntConsumer`, `ThrowingDoubleConsumer`, `ThrowingLongConsumer`\n- `ThrowingIntFunction`, `ThrowingDoubleFunction`, `ThrowingLongFunction`\n- `ThrowingIntPredicate`, `ThrowingDoublePredicate`, `ThrowingLongPredicate`\n- `ThrowingIntSupplier`, `ThrowingDoubleSupplier`, `ThrowingLongSupplier`\n- and so on...\n\n\u003e just find what you want.\n\n### SPI\n\nusage\n\nuse `@SPI` to annotate it is a SPI interface\n\n```java\n@SPI\npublic interface Shape {\n    // ...\n}\n```\n\nuse `@Feature` to add properties for implementation\n\n```java\n@Feature(groups = \"foo\", order = 1, tags = \"a:1\", excludeTags = \"b:1\")\npublic class Circle implements Shape {\n    // ...\n}\n@Feature(groups = \"bar\", order = -1, tags = \"a:2\", excludeTags = \"b:2\")\npublic class Triangle implements Shape {\n    // ...\n}\n```\n\nadd spec file to `META-INF/services/`, or `META-INF/esa/`, or  `META-INF/esa/internal/`(any directory is ok)\n\nso we add a spec file\n\n\u003e META-INF/services/io.esastack.commons.Shape\n\u003e\n\u003e ```\n\u003e io.esastack.commons.Circle\n\u003e io.esastack.commons.Triangle\n\u003e ```\n\nuse `SpiLoader` to get SPI extensions\n\ninstances should be in sort by `@Feature#order`\n\n```java\nfinal List\u003cShape\u003e shapes = SpiLoader.cached(Shape.class).getAll();\nassertEquals(2, shapes.size());\nassertTrue(shapes.get(0) instanceof Triangle);\nassertTrue(shapes.get(1) instanceof Circle);\n```\n\nget by groups\n\n```java\nfinal List\u003cShape\u003e shapes = SpiLoader.cached(Shape.class).getByGroup(\"foo\");\nassertEquals(1, shapes.size());\nassertTrue(shapes.get(0) instanceof Circle);\n```\n\nget by tags\n\n```java\nfinal List\u003cShape\u003e shapes = SpiLoader.cached(Shape.class).getByTags(Collections.singletonMap(\"a\", \"2\"));\nassertEquals(1, shapes.size());\nassertTrue(shapes.get(0) instanceof Triangle);\n```\n\nget by features\n\n```java\nfinal List\u003cShape\u003e shapes = SpiLoader.cached(Shape.class)\n        .getByFeature(\"foo\", Collections.singletonMap(\"a\", \"1\"));\nassertEquals(1, shapes.size());\nassertTrue(shapes.get(0) instanceof Circle);\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesastack%2Fesa-commons","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fesastack%2Fesa-commons","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesastack%2Fesa-commons/lists"}