So, sometimes when you have an existing software structure, with lots of domain rules inside, it becomes difficult to change. Especially when there is a lot of cohesion between your classes.
This is the problem of tight coupling, as opposed to loose coupling1.
I recently encountered this in our software.
And of course, I needed to change something deep inside.
Now, I didn't need to change anything about the domain logic. It was very good and working as intended.
But I needed a change purely for technical reasons. In this case, the domain logic was used in a Batch Processing on Hibernate Entities, and the domain logic was (in this case, not normally) generating a lot of Entities, and they were all persisted in the Session Context, which naturally grew. And then the dirty check and the session closing takes a long time, etc, etc. See reference [2] on why this's bad.
So I was looking for a way to persist entities, without adding them to all the collections with the CascadeType without changing the domain logic too much, and also keep the default behaviour as standard.
I didn't want to use inheritance, as it was a persisted Hibernate Entity.
So, I came up with the following hack:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Quite simply, I can now add behaviour to an Entity when I need it.
For example:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
As me and my colleague say "A seemingly innocent first step on a slippery slope to hell."
Of course, this kind of abuse has always been possible, but it is now easier to do than ever (without having to create an entire class for the priviledge).
I recently had to do a insert on the database, if the records weren't there, and an update if it was.
Performance was a problem.
Colleague of mine had the same problem when using an insert and update statement with an (exists (select 1)) on 2.5 million records.
After 5 minutes, 5000 rows were done. It would have taken 41 hours to fix them all.
After a merge1 statement, the entire thing was done in 2.5 minutes.
So, I had to get to grips with the merge statement as well. I hadn't used it before.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Especially for first-timers one has a tendency to use the wrong methods.
Take the following sequence of refactoring for example:
First without any kind of Optional:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
It looks like the person just... kind of removed the Optional back to a situation that he knew and was more familiar with.
Let's try this again:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Now this is a good try. It is very common to see the two Optional class methods being used together. isPresent() and get().
But we can do better.
It's often the same: we wish to do something, only when there's a value available.
Like so:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Of course, it's sometimes better to create a method reference:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some time ago, I received a security notification from Github regarding a vulnerability1 2 in Antisamy.
It took me a while to find out that it was a false positive.
But the issue in [1] did mention that Antisamy seems to be lacking a maintainer. A worthwhile replacement was suggested as being HTML Sanitizer3 5 6.
I started using the HTML Sanitizer in my project and I like it. The fact that I don't need an XML configuration file is a plus.
There's a StackOverflow answer on why and how of the two projects4.
Only thing missing are the policy configuration files from AntiSamy, which are not present in any way in HTML Sanitizer, so you either have to write your own or get them from here7.
The file is called HTMLSanitizerAntiSamyPolicy.java.