My resolution for the last couple of years has been to share any code I write that could possibly be of use (and, of course, isn't proprietary) with the community. The hope is that one of those tidbits can help somebody solve something, and maybe start a spark that can get something widespread going.
As a part of this, I've been doing a bit of (for lack of a better word) "backsliding" to writing Java code. As an ardent supporter of the Groovy language (and most other JVM languages), I rarely use the "mother language" to complete my day-to-day tasks. But one thing I've realized as I've tried to get involved with the JVM-based community at large is that not everybody has this luxury. Or indeed this opinion. As much as I'd love to write all of my library code in Groovy, I realize that you're asking a little something of your users if you force them to include Groovy and its associated libraries as one of their dependencies. Taking the long way around with Java results in a much cleaner set of dependencies for the end user.
A library I offered recently was a Java implementation of the wildly-complicated "National Retail Federation 4-5-4 Calendar." When I wrote it initially, it took advantage of Groovy's date shortcuts, with things like:
Date aWeekLater = date + 6
There were a few bumps in the road with Map interactions, but the conversion over to Java was pretty painless. To be honest, it was almost comforting in a way, like reminiscing about the old days with a friend. I used to dutifully rattle off the long, repetitive lines (with the requisite semicolon at the end) and have that intuition of where I had to stick that cast statement to make things compile.
The other plus is that you can use Spock to test the code, since it's all going to be bytecode anyway. The library user can simply ignore the unit tests.
I had a small sense of cockiness as I proceeded to my next conversion task (all of my code starts out as Groovy). As part of a reporting suite I built, I put together a Java wrapper for Adobe's absolutely atrocious SiteCatalyst API. I did my best to unfurl the insanely complex rat's nest of JSON that they return. It was easy enough with Groovy -- JsonSlurper and JsonBuilder could marshal and unmarshal with the greatest of ease. I decided it was time to unleash to the world, since I no longer had use for it, and also invested a pretty large amount of time figuring out how to get meaningful data out of Adobe's analytics vault.
This where I started to appreciate the strength of Groovy. With the single groovy-all dependency, you get everything. Fantastic JSON support, and all of the wonderful shorthand for map access. The work on this conversion is still in progress, but here's an example of where you can really save yourself with Groovy:
def requestStructure = [reportDescription: [
reportSuiteID : omnitureReport.reportSuiteID,
dateFrom : omnitureReport.dateFrom.format(OMNITURE_REPORT_DATE_FORMAT),
dateTo : omnitureReport.dateTo.format(OMNITURE_REPORT_DATE_FORMAT),
metrics : omnitureReport.metrics.
collectAll {[id: it.id, segments: it.segmentId ? [[id: it.segmentId]] : null]},
sortBy : omnitureReport.sortBy,
dateGranularity: omnitureReport.granularity?.value(),
elements : omnitureReport.elements?.collectAll {
[id : it.id,
top : it.limit == -1 ? omnitureReport.limit : it.limit,
search : getSearchKeywords(it.elementTypeAndKeywordFilter),
classification: it.classification ?: "",
startingWith : it.startingWith,
]
},
segments : omnitureReport.segmentIds ? omnitureReport.segmentIds.collect{[id:it]} : null
]]
Nothing complicated, right? In one statement, I'm building up the structure of a report with a handful of succinct expressions. Let's just take a peek at how we'd do this in Java:
DateFormat dateFormat = new SimpleDateFormat(OMNITURE_REPORT_DATE_FORMAT);
Map<Map<String, ?>> requestStructure = new HashMap<>();
Map<String, ?> reportDescription = new HashMap<>();
requestStructure.put("reportDescription", reportDescription);
reportDescription.put("reportSuiteID", omnitureReport.getReportSuiteID());
reportDescription.put("dateFrom", dateFormat.format(omnitureReport.getDateFrom());
This is all pretty doable, but where the rails really come off are on some of the compound statements:
omnitureReport.metrics.collectAll { [id: it.id, segments: segmentId? [[id: it.segmentId]] : null ]}
How does this look in Java? I'd start by busting it out into a method:
private List<Map<String, ?>> getMetrics(List<OmnitureRequestMetrics> metricsIn) {
List<Map<String, ?>> result = new ArrayList<>();
for (OmnitureRequestMetric metric: metricsIn) {
Map<String, ?> row = new HashMap<>();
result.add(row);
row.put("id", metric.getId());
if (metric.segmentId != null) {
row.put("segments", Collections.singletonList(Collections.singletonMap("id", metric.getSegmentId()));
} else {
row.put("segments", Collections.EMPTY_MAP);
}
}
}
And frankly, it only gets worse from here.
Thankfully, there are some wonderful JSON libraries out there, namely Jackson, which makes marshaling and unmarshaling pretty simple. Not as simple as in Groovy, I'd dare say, but at least palatable.
I suppose I should go and keep working on this conversion task...