Skip to content

Commit ebe71e9

Browse files
committed
(maint) Update documentation on how to use configuration
1 parent 1417028 commit ebe71e9

File tree

5 files changed

+478
-25
lines changed

5 files changed

+478
-25
lines changed

README.adoc

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
== Stringify Object for Java
2+
3+
https://travis-ci.org/wavesoftware/java-stringify-object[image:https://travis-ci.org/wavesoftware/java-stringify-object.svg?branch=master[Build
4+
Status]]
5+
https://sonar.wavesoftware.pl/dashboard/index/pl.wavesoftware.utils:stringify-object[image:https://sonar.wavesoftware.pl/api/badges/gate?key=pl.wavesoftware.utils:stringify-object[Quality
6+
Gate]]
7+
https://coveralls.io/github/wavesoftware/java-stringify-object?branch=master[image:https://coveralls.io/repos/github/wavesoftware/java-stringify-object/badge.svg?branch=master[Coverage
8+
Status]]
9+
https://bintray.com/bintray/jcenter/pl.wavesoftware.utils%3Astringify-object[image:https://img.shields.io/maven-central/v/pl.wavesoftware.utils/stringify-object.svg[Maven
10+
Central]]
11+
12+
A utility to safely inspect any Java Object as String representation.
13+
It's best to be used with domain model (also with JPA entities) with
14+
intention to dump those entities as text to log files.
15+
16+
It runs in two modes: `+PROMISCUOUS+` (by default) and `+QUIET+`. In
17+
`+PROMISCUOUS+` mode every defined field is automatically inspected,
18+
unless the field is annotated with `+@DoNotInspect+` annotation. In
19+
`+QUIET+` mode only fields annotated with `+@Inspect+` will gets
20+
inspected.
21+
22+
This library has proper support for object graph cycles, and JPA
23+
(Hibernate) lazy loaded elements.
24+
25+
=== Usage
26+
27+
==== In Promiscuous mode
28+
29+
[source,java]
30+
----
31+
// In PROMISCUOUS mode define fields to exclude
32+
class Person {
33+
private int id;
34+
@DisplayNull
35+
private Person parent;
36+
private List<Person> childs;
37+
private Account account;
38+
@Inspect(conditionally = IsInDevelopment.class)
39+
private String password;
40+
@DoNotInspect
41+
private String ignored;
42+
}
43+
44+
// inspect an object
45+
List<Person> people = query.getResultList();
46+
Stringify stringify = Stringify.of(people);
47+
stringify.mode(Mode.PROMISCUOUS);
48+
// stringify.beanFactory(...);
49+
assert "<Person id=15, parent=<Person id=16, parent=null, "
50+
+ "childs=[(↻)], account=⁂Lazy>, childs=[], "
51+
+ "account=⁂Lazy>".equals(stringify.toString());
52+
----
53+
54+
==== In Quiet mode
55+
56+
[source,java]
57+
----
58+
// In QUIET mode define fields to inspect
59+
class Person {
60+
@Inspect private int id;
61+
@Inspect @DisplayNull private Person parent;
62+
@Inspect private List<Person> childs;
63+
@Inspect private Account account;
64+
private String ignored;
65+
}
66+
67+
// inspect an object
68+
List<Person> people = query.getResultList();
69+
Stringify stringify = Stringify.of(people);
70+
stringify.mode(Mode.QUIET);
71+
assert "<Person id=15, parent=<Person id=16, parent=null, "
72+
+ "childs=[(↻)], account=⁂Lazy>, childs=[], "
73+
+ "account=⁂Lazy>".equals(stringify.toString());
74+
----
75+
76+
=== Features
77+
78+
* String representation of any Java class in two modes `+PROMISCUOUS+`
79+
and `+QUIET+`
80+
* Fine tuning of which fields to display
81+
* Support for cycles in object graph - `+(↻)+` is displayed instead
82+
* Support for Hibernate lazy loaded entities - `+⁂Lazy+` is displayed
83+
instead
84+
85+
[[vs-lombok-tostring]]
86+
=== vs. Lombok @ToString
87+
88+
Stringify Object for Java is designed for *slightly different* use case
89+
then Lombok.
90+
91+
Lombok `+@ToString+` is designed to quickly inspect fields of simple
92+
objects by generating static simple implementation of this mechanism.
93+
94+
Stringify Object for Java is designed to inspect complex objects that
95+
can have cycles and can be managed by JPA provider like Hibernate
96+
(introducing Lazy Loading problems).
97+
98+
==== Pros of Lombok vs Stringify Object
99+
100+
* Lombok is *fast* - it's statically generated code without using
101+
Reflection API.
102+
* Lombok is *easy* - it's zero configuration in most cases.
103+
104+
==== Cons of Lombok vs Stringify Object
105+
106+
* Lombok can't *detect cycles* is object graph, which implies
107+
`+StackOverflowException+` being thrown in that case
108+
* Lombok can't detect a *lazy loaded entities*, which leads to force
109+
loading it from JPA by invoking SQL statements. It's typical *n+1
110+
problem*, but with nasty consequences - your `+toString()+` method is
111+
invoking SQL without your knowledge!!
112+
113+
=== Configuration
114+
115+
Configuration is done in two ways: declarative - using Java's service
116+
loader mechanism, and programmatic.
117+
118+
==== Configuration using Service Loader
119+
120+
A `+Configurator+` interface is intended to be implemented in user code,
121+
and assigned to https://www.baeldung.com/java-spi[Service Loader]
122+
mechanism.
123+
124+
To do that, create on your classpath, a file:
125+
126+
`+/META-INF/services/pl.wavesoftware.utils.stringify.spi.Configurator+`
127+
128+
In that file, place a fully qualified class name of your class that
129+
implements `+Configurator+` interface. It should be called first time
130+
you use an Stringify to inspect an object:
131+
132+
....
133+
# classpath:/META-INF/services/pl.wavesoftware.utils.stringify.spi.Configurator
134+
org.acmecorp.StringifyConfigurator
135+
....
136+
137+
Then implement that class in your code:
138+
139+
[source,java]
140+
----
141+
package org.acmecorp;
142+
143+
import pl.wavesoftware.utils.stringify.api.Configuration;
144+
import pl.wavesoftware.utils.stringify.spi.Configurator;
145+
146+
public final class StringifyConfigurator implements Configurator {
147+
148+
@Override
149+
public void configure(Configuration configuration) {
150+
configuration.beanFactory(new SpringBeanFactory());
151+
}
152+
}
153+
----
154+
155+
with example Spring based BeanFactory:
156+
157+
[source,java]
158+
----
159+
package org.acmecorp;
160+
161+
import org.springframework.context.event.ContextRefreshedEvent;
162+
import org.springframework.context.ApplicationContext;
163+
import org.springframework.context.annotation.Configuration;
164+
165+
import pl.wavesoftware.utils.stringify.spi.BeanFactory;
166+
import pl.wavesoftware.utils.stringify.spi.BootingAware;
167+
168+
@Configuration
169+
class SpringBeanFactory implements BeanFactory, BootingAware {
170+
private static ApplicationContext context;
171+
172+
@EventListener(ContextRefreshedEvent.class)
173+
void onRefresh(ContextRefreshedEvent event) {
174+
SpringBeanFactory.context = event.getApplicationContext();
175+
}
176+
177+
@Override
178+
public <T> T create(Class<T> contractClass) {
179+
return SpringBeanFactory.context.getBean(contractClass);
180+
}
181+
182+
@Override
183+
public boolean isReady() {
184+
return SpringBeanFactory.context != null;
185+
}
186+
}
187+
----
188+
189+
==== Programmatic configuration
190+
191+
You can also fine tune you configuration on instance level - using
192+
methods available at `+Stringify+` interface:
193+
194+
[source,java]
195+
----
196+
// given
197+
BeanFactory beanFactory = createBeanFactory();
198+
Person person = createPerson();
199+
200+
// then
201+
Stringify stringifier = Stringify.of(person);
202+
stringifier
203+
.beanFactory(beanFactory)
204+
.mode(Mode.QUIET)
205+
.stringify();
206+
----
207+
208+
=== Dependencies
209+
210+
* Java >= 8
211+
* https://github.com/wavesoftware/java-eid-exceptions[EID Exceptions]
212+
library
213+
214+
==== Contributing
215+
216+
Contributions are welcome!
217+
218+
To contribute, follow the standard
219+
http://danielkummer.github.io/git-flow-cheatsheet/[git flow] of:
220+
221+
. Fork it
222+
. Create your feature branch
223+
(`+git checkout -b feature/my-new-feature+`)
224+
. Commit your changes (`+git commit -am 'Add some feature'+`)
225+
. Push to the branch (`+git push origin feature/my-new-feature+`)
226+
. Create new Pull Request
227+
228+
Even if you can't contribute code, if you have an idea for an
229+
improvement please open an
230+
https://github.com/wavesoftware/java-stringify-object/issues[issue].

README.md

100644100755
+99-8
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ class Person {
3030

3131
// inspect an object
3232
List<Person> people = query.getResultList();
33-
ObjectStringifier stringifier = new ObjectStringifier(people);
34-
stringifier.setMode(Mode.PROMISCUOUS);
35-
// stringifier.setBeanFactory(...);
33+
Stringify stringify = Stringify.of(people);
34+
stringify.mode(Mode.PROMISCUOUS);
35+
// stringify.beanFactory(...);
3636
assert "<Person id=15, parent=<Person id=16, parent=null, "
3737
+ "childs=[(↻)], account=⁂Lazy>, childs=[], "
38-
+ "account=⁂Lazy>".equals(stringifier.toString());
38+
+ "account=⁂Lazy>".equals(stringify.toString());
3939
```
4040

4141
### In Quiet mode
@@ -51,11 +51,11 @@ class Person {
5151

5252
// inspect an object
5353
List<Person> people = query.getResultList();
54-
ObjectStringifier stringifier = new ObjectStringifier(people);
55-
stringifier.setMode(Mode.QUIET);
54+
Stringify stringify = Stringify.of(people);
55+
stringify.mode(Mode.QUIET);
5656
assert "<Person id=15, parent=<Person id=16, parent=null, "
5757
+ "childs=[(↻)], account=⁂Lazy>, childs=[], "
58-
+ "account=⁂Lazy>".equals(stringifier.toString());
58+
+ "account=⁂Lazy>".equals(stringify.toString());
5959
```
6060

6161
## Features
@@ -83,9 +83,100 @@ Stringify Object for Java is designed to inspect complex objects that can have c
8383
* Lombok can't **detect cycles** is object graph, which implies `StackOverflowException` being thrown in that case
8484
* Lombok can't detect a **lazy loaded entities**, which leads to force loading it from JPA by invoking SQL statements. It's typical **n+1 problem**, but with nasty consequences - your `toString()` method is invoking SQL without your knowledge!!
8585

86+
## Configuration
87+
88+
Configuration is done in two ways: declarative - using Java's service loader mechanism,
89+
and programmatic.
90+
91+
### Configuration using Service Loader
92+
93+
A `Configurator` interface is intended to be implemented in user code, and assigned
94+
to [Service Loader](https://www.baeldung.com/java-spi) mechanism.
95+
96+
To do that, create on your classpath, a file:
97+
98+
`/META-INF/services/pl.wavesoftware.utils.stringify.spi.Configurator`
99+
100+
In that file, place a fully qualified class name of your class that implements
101+
`Configurator` interface. It should be called first time you use an Stringify to inspect
102+
an object:
103+
104+
```
105+
# classpath:/META-INF/services/pl.wavesoftware.utils.stringify.spi.Configurator
106+
org.acmecorp.StringifyConfigurator
107+
```
108+
109+
Then implement that class in your code:
110+
111+
```java
112+
package org.acmecorp;
113+
114+
import pl.wavesoftware.utils.stringify.api.Configuration;
115+
import pl.wavesoftware.utils.stringify.spi.Configurator;
116+
117+
public final class StringifyConfigurator implements Configurator {
118+
119+
@Override
120+
public void configure(Configuration configuration) {
121+
configuration.beanFactory(new SpringBeanFactory());
122+
}
123+
}
124+
```
125+
126+
with example Spring based BeanFactory:
127+
128+
```java
129+
package org.acmecorp;
130+
131+
import org.springframework.context.event.ContextRefreshedEvent;
132+
import org.springframework.context.ApplicationContext;
133+
import org.springframework.context.annotation.Configuration;
134+
135+
import pl.wavesoftware.utils.stringify.spi.BeanFactory;
136+
import pl.wavesoftware.utils.stringify.spi.BootingAware;
137+
138+
@Configuration
139+
class SpringBeanFactory implements BeanFactory, BootingAware {
140+
private static ApplicationContext context;
141+
142+
@EventListener(ContextRefreshedEvent.class)
143+
void onRefresh(ContextRefreshedEvent event) {
144+
SpringBeanFactory.context = event.getApplicationContext();
145+
}
146+
147+
@Override
148+
public <T> T create(Class<T> contractClass) {
149+
return SpringBeanFactory.context.getBean(contractClass);
150+
}
151+
152+
@Override
153+
public boolean isReady() {
154+
return SpringBeanFactory.context != null;
155+
}
156+
}
157+
```
158+
159+
### Programmatic configuration
160+
161+
You can also fine tune you configuration on instance level - using methods available at
162+
`Stringify` interface:
163+
164+
```java
165+
// given
166+
BeanFactory beanFactory = createBeanFactory();
167+
Person person = createPerson();
168+
169+
// then
170+
Stringify stringifier = Stringify.of(person);
171+
stringifier
172+
.beanFactory(beanFactory)
173+
.mode(Mode.QUIET)
174+
.stringify();
175+
```
176+
86177
## Dependencies
87178

88-
* Java >= 7
179+
* Java >= 8
89180
* [EID Exceptions](https://github.com/wavesoftware/java-eid-exceptions) library
90181

91182
### Contributing

0 commit comments

Comments
 (0)