Project split from directory https://github.com/Hubbitus/ImapTree/tree/master/src/info/hubbitus/imaptree/utils into separate repository to do not copy/paste it many times.
The main purpose to collect there useful utility classes like ProgressLogger, ConfigExtended and share them between Groovy/Java projects..
Helper class logger of progress operation. It is intended for easy add possibility of logging progress of operations.
-
For example in Groovy way it so simple as:
ProgressLogger.each([1, 2, 3, 4, 5]){ println it // Some long run operation }
It will produce (by println) output like:
Process Integer #1 from 5 (20,00%). Spent (pack by 1) time: 0,041 (from start: 0,047) 1 Process Integer #2 from 5 (40,00%). Spent (pack by 1) time: 0,001 (from start: 0,278), Estimated items: 3, time: 0,417 2 Process Integer #3 from 5 (60,00%). Spent (pack by 1) time: 0,012 (from start: 0,330), Estimated items: 2, time: 0,220 3 Process Integer #4 from 5 (80,00%). Spent (pack by 1) time: 0,001 (from start: 0,340), Estimated items: 1, time: 0,085 4 Process Integer #5 from 5 (100,00%). Spent (pack by 1) time: 0,001 (from start: 0,344) 5 -
Ofter useful provide out method, for example to tie into current scope logger instead of global stdout, and add some additional transform, it also simple:
ProgressLogger.each([1, 2, 3, 4, 5]){ println it }{ log.info "=$it=" }
-
It may be used directly in plain old Java style like:
ProgressLogger pl = new ProgressLogger(aisList, {log.info(it)}) for (Object aisObj in aisList){ pl.next() try{ rowMapper(aisObj, di) } catch(AsaException ae){ commonErrorAdd(di, ae) } }
-
Or measure one run:
ProgressLogger.measure({log.info(it)}, { /* long work */ }, 'Doing cool work')
Or:
def result = ProgressLogger.measureAndLogTime({spent-> log.debug('Operation took: ' + spent) }){ println 'test' // Some long measured work return 42 } assert 42 == result
-
When amount of elements or iterations is not known (f.e. stream processing or recursive calls like tree traversal) totalAmountOfElements set to -1 and simpler statistic returned:
def pl = new ProgressLogger() [1, 2, 3, 4].each{ sleep 1000 pl.next() }
Result will be something like:
Process item #1. Spent (pack by 1) time: 1,007 (from start: 1,007) Process item #2. Spent (pack by 1) time: 1,000 (from start: 2,055) Process item #3. Spent (pack by 1) time: 1,001 (from start: 3,058) Process item #4. Spent (pack by 1) time: 1,001 (from start: 4,061) You are able provide result messages, custom message formatting, pack size after precessing write log, auto adjusting such pack size to do not spam log, provide custom logging methods and so on.
Main goal to add functionality to ConfigObject GDK class.
First it allow operations opposite flatten, set hierarchy from string, like:
conf.setFromPropertyPathLikeKey('some.deep.hierarchy.of.properties', value)and then access it as usual:
conf.some.deep.hierarchy.of.propertiesnot as it is one string property.
conf.'some.deep.hierarchy.of.properties'Additionally, it overrides merge of ConfigObjects and do not replace completely replace Objects but set properties of it.
For example standard behaviour:
// Uncomment next line if you are plan run example from GroovyConsole to handle defined there classes: http://groovy.329449.n5.nabble.com/GroovyConsole-and-context-thread-loader-td4471707.html
// Thread.currentThread().contextClassLoader = getClass().classLoader
@groovy.transform.ToString
class Test{
String s = 's initial'
Integer i = 77
}
ConfigObject config = new ConfigSlurper().parse('''config{
some.property = 'value'
test = new Test()
}''').config
ConfigObject config1 = new ConfigSlurper().parse('''config{ test.s = 's change' }''').config
config.merge(config1)
assert config.test.s == 's change'But stop, why config.test was replaced? Our intention was to set only their field s!
That class do that magic
ConfigExtended configExtended = ConfigExtended.create('''config{ one = 1 }''')
configExtended.notExistent << 'list value'
configExtended.notExistent instanceof ListIt useful to do not extra checks in cycle, f.e.:
(1..100).each{
if (!configExtended.isSet('listValues')) configExtended.listValues = []
configExtended.listValues.add(it)
}became just:
(1..100).each{
configExtended.listValues.add(it)
}and result identical!
Singleton class for automatically load Config.groovy or config.groovy config from current resources and dispose them as singleton.
Idea to simplify access you configuration in simple scripts application when no IOC/DI used.
F.e. in resources you have file Config.groovy with content:
config {
some {
property = 'qwerty'
}
}Then you may just do in script without any initialization:
println GlobalConfig.instance.some.propertyPlease note, file must include config{} closure on top level which will be stripped automatically.
Just add dependency like:
<dependency>
<groupId>info.hubbitus</groupId>
<artifactId>Hubbitus-common</artifactId>
<version>1.7</version>
</dependency>implementation 'info.hubbitus:Hubbitus-common:1.7'
- Add possibility to parametrize object name by iteration number or list value for
ProgressLogger.eachcall. Seeinfo.hubbitus.utils.bench.ProgressLoggerJavaTest.listEachStaticfor example - Add autotests run on CI
- Extend testing, including:
- Java tests with lombok auto-created
logfields
- Java tests with lombok auto-created
- Update gradle to version 6.7, groovy to 4.0.6
- Improve GlobalConfig to log warning when config not found, make more robust
- Minor test fir
- Add method
ProgressLogger.measureAndLogTime
- Add class
GlobalConfigfor easy access configuration from resources. - Update gradlew version to 4.7.
- Adopt test to be locale independent.
-
Add test
info.hubbitus.utils.ConfigExtendedTest -
Add method
info.hubbitus.utils.ConfigExtended.setFromPropertyPathLikeKey(java.lang.String, java.lang.String)to do not always quoteStringvalues -
And
factory methodinfo.hubbitus.utils.ConfigExtended.create(java.lang.String, java.lang.String)to do not always remember useConfigSlurperand cast result.So now instead of:
ConfigExtended configExtended = ((ConfigExtended)new ConfigSlurper().parse('config{ one = 1 }')).config
you may just do:
ConfigExtended.create('config{ one = 1 }')
- Add
info.hubbitus.utils.ConfigExtended.leftShiftoperator (<< operation) for easy list creation
- Fix
ProgressLogger.measurewith object result from closure
- Fix benchmark. Add test
- Make
ProgressLoggerthread safe, add parallel test with GParse next(Closure toRun)method now returns result fromtoRunclosure. So you may use it directly n collection methods likecollect:
List list = [1, 2, 3]
ProgressLogger pl = new ProgressLoger(list);
def res = list.collect{
pl.next{
it * 2
}
}
assert res = [2, 4, 6]
- Step to use
Producer/Consumerfunctional interfaces instead of justClosureas arguments to be closer to Java calls! Before that you had to create instance ofClosure. It works, but has two drawbacks: - It can't be simplified by
lambdausage - In logback loggers, when method accounted
.call()main method logged - Java test initiated also
So, now in pure Java instead of ugly:
ProgressLogger pl = new ProgressLogger(list, new Closure(null) {
@Override
public Object call(Object... args) {
log.debug((String) args[0]);
return null;
}
});
you may simply do:
ProgressLogger pl = new ProgressLogger(list, it -> log.debug(it));
which much more cleaner.
- Step to gradle 4.1