This is an excellent use of Objenesis. Small, to the point, useful,
and does one thing really well.
I'd like to blog about it and link to it from the Objenesis website.
But first some feedback on some potential improvements to make it more
suitable as a library:
* Using static fields for configuration data (CloneUtils.ignored) and
caching (CloneUtils.objectInstantiators) creates hidden dependencies
that can lead to bugs in runtime systems. For example, suppose two
different parts of an application (or dependencies) attempt to use
CloneUtils in different ways, they could tread on each others feet.
Instead, ensure that CloneUtils can be instantiated, use non-static
fields, and allow the apps that call them to isolate them as they
wish. Of course, you could still provide a static helper class for
users who don't care, but you wouldn't want to force people to do
this.
* By removing the staticness of the fields, you can also do the same
with the methods, allowing power-users to override just parts of it
(e.g. if they wanted a custom newInstance() method).
* Would be handy to allow users to use their own instance of
Objenesis. You could provide an additional constructor or a setter to
allow this.
* I'd steer clear from using 'Util' in a class name of a public API.
How about calling the class 'Cloner'?
* clone0() seems a strange name for a public method. How about just
calling it clone() and allow overloading to take effect. Or maybe name
it more descriptively.
* You can simplify usage for the end user by embedding Objenesis
inside cloning.jar (under com.rits.beans.objenesis to ensure
isolation). This means users only need to include your jar and no
other dependencies. See
http://objenesis.googlecode.com/svn/docs/embedding.html
* JavaDoc on the site would make it easier for people to use. Hmmm...
Objenesis is lacking JavaDoc on the main site too - we should fix that
;)
* It would be simpler for newInstance() to simply call
objenesis.newInstance() rather than looking up the ObjectInstantiator
and caching it, as this is exactly what Objenesis does under the hood
(so there's no performance improvement). Though I see you've used a
ConcurrentHashMap, which I assume is why you've done this. I reckon we
should update Objenesis to do the same. Good idea.
Sounds like a lot of negative feedback, but believe me, that's not how
I mean it. I'm very glad to see this library and I'm just offering
some suggestions to make it even better :).
thanks
-Joe
Hi Kostas This is an excellent use of Objenesis. Small, to the point, useful, and does one thing really well. I'd like to blog about it and link to it from the Objenesis website. But first some feedback on some potential improvements to make it more suitable as a library: * Using static fields for configuration data (CloneUtils.ignored) and caching (CloneUtils.objectInstantiators) creates hidden dependencies that can lead to bugs in runtime systems. For example, suppose two different parts of an application (or dependencies) attempt to use CloneUtils in different ways, they could tread on each others feet. Instead, ensure that CloneUtils can be instantiated, use non-static fields, and allow the apps that call them to isolate them as they wish. Of course, you could still provide a static helper class for users who don't care, but you wouldn't want to force people to do this.
* By removing the staticness of the fields, you can also do the same with the methods, allowing power-users to override just parts of it (e.g. if they wanted a custom newInstance() method).
* Would be handy to allow users to use their own instance of Objenesis. You could provide an additional constructor or a setter to allow this.
* I'd steer clear from using 'Util' in a class name of a public API. How about calling the class 'Cloner'? * clone0() seems a strange name for a public method. How about just calling it clone() and allow overloading to take effect. Or maybe name it more descriptively.
* You can simplify usage for the end user by embedding Objenesis inside cloning.jar (under com.rits.beans.objenesis to ensure isolation). This means users only need to include your jar and no other dependencies. See http://objenesis.googlecode.com/svn/docs/embedding.html
* JavaDoc on the site would make it easier for people to use. Hmmm... Objenesis is lacking JavaDoc on the main site too - we should fix that ;)
* It would be simpler for newInstance() to simply call objenesis.newInstance() rather than looking up the ObjectInstantiator and caching it, as this is exactly what Objenesis does under the hood (so there's no performance improvement). Though I see you've used a ConcurrentHashMap, which I assume is why you've done this. I reckon we should update Objenesis to do the same. Good idea.
Sounds like a lot of negative feedback, but believe me, that's not how I mean it. I'm very glad to see this library and I'm just offering some suggestions to make it even better :).
I've just looked over it again, and it's looking really nice now.
Excellent work.
A few more suggestions:
* cloneSoftenException(). I would maybe make this the default method.
Of the 2 exceptions it wraps, IllegalArgumentException is already a
RuntimeException and IllegalAccessException is an internal
implementation detail that should probably never be exposed. I would
wrap it in a custom exception (e.g. CloneException, which extends
RuntimeException). You'd only need one clone() method then.
That's it for the actual code. The remaining comments are about distribution.
* Pick a license. LICENSE.txt currently contains a whole load of
licenses. Make the license clear on the website. I usally choose
Apache License v2, but any of the main ones should do.
* Include the tests, source (uncompressed), docs and build file in the download.
* Also provide a link on the website directly to the binary .jar file.
* Create a mailing list for users. You may even want to use a full on
code hosting site like Google Code or SourceForge.
* Blatent optional plug: You can use http://xsite.codehaus.org/ to
help you generate a familiar-looking pretty site.
Ship it :)
After the Joe's excellent review, I'll go with some comments on my
own. Most on Objenesis in fact.
First, great job. Your library will definitly be useful. And please
pick a license ASAP to allow it to be used by everyone wihout any
legal unknown. Deploy it in the maven central repository also a must.
1- There's one drawback about embedding Objenesis (even if it's what I
currently do on EasyMock). Since the package changes, if you create or
use someone else InstantiatorStrategy it needs to implement the
interface in the package of the embedding library instead of in
Objenesis genuine package. It then limits reusability.
2- True... javadoc isn't on Objenesis site... Should be
3- I didn't used a ConcurrentHashMap because I was necessary to
synchronized getInstantiatorOf anyway. So there was no point to add a
layer of synchronization.
4- http://robust-it.co.uk/clone/cloning-1.0.zip doesn't exist :-(
5- I couldn't look at the code because of 4 but how do you find out a
class is immutable?
6- I think a undeep clone can also be useful
7- xsite is great :-)
8- Since it's still the season, happy new year everyone :-)
Cheers,
Henri
On Sonntag, 18. Januar 2009, Konstantine Kougios wrote:
[snip]
> About #5, I know of no simple way to determine which classes are
> immutable. So it is simply implemented for some immutable jdk classes:
>
> if (clz == String.class || clz == Integer.class || clz ==
> Long.class || clz == Boolean.class || clz == Class.class || clz ==
> Float.class || clz == Double.class || clz == Character.class || clz ==
> Byte.class || clz == Short.class || clz == Void.class || clz ==
> BigDecimal.class || clz == BigInteger.class) return o;
>
> This speeds up things a lot and uses less memory. (I use the lib in a
> high traffic site. I use it for a cache of 25000 items, cloning the
> cached objects. Cloning strings was taking time and was also using
> memory for no good reason)
There are some more like the complete enum stuff, URI, URL, UUID, the
java.util.ref stuff, the a lot of the reflection stuff, Pattern ... actually
it would be nice to be able to add own types or set the immutable types list
completely.
> About #6, shallowClone() would be handy, I know. TODO
Please also make some notes about limitations. I'd expect problems cloning
structures that contain system resources like sockets, pipes or even worse
implementations that keep a pointer to native memory (like internals of the
BufferedImage - you can wreck your VM).
- Jörg
Another question Kostas, when dontClone is called, is it still copying
the reference? If yes, it means that a dontClone class is exactly like
an immutable class. So you could have only dontClone. However, I think
it could be useful to be able to specify classes that shouldn't be
cloned nor copy as references (and possibly bean path that should be
ignored).