Tales from the jar side: A 300 joke, a YouTube livestream, I make way too many mistakes but at least I don't file legal briefs based on ChatGPT, and the usual Tweets and Toots
The rotation of the Earth really makes my day. (rimshot)
Welcome, fellow jarheads, to Tales from the jar side, the Kousen IT newsletter, for the week of May 21 - May 28, 2023. This week I held a live stream on the Tales from the jar side YouTube channel and recorded other videos for my Spring and Spring Boot course on the O’Reilly Learning Platform.
Regular readers of (and listeners to, and now video viewers of) this newsletter are affectionately known as jarheads, and are far more intelligent, sophisticated, and attractive than the average newsletter reader (or listener, or viewer). If you wish to become a jarhead, please subscribe using this button:
As a reminder, when this message is truncated in email, click on the title to open it in a browser tab formatted properly for both the web and mobile.
A 300 Joke
This image says it all:
As you can hopefully see, my YouTube channel hit 300 subscribers. I felt compelled to make this obvious reference:
Hopefully no viewers of my channel will get massacred by an invading army of Persians, only to be bailed out by the Athenian navy later.
(As of this writing, I’m now up to 306. Onward and upward.)
YouTube Live Stream
On Wednesday, I held a live event on both YouTube and LinkedIn, featuring Greg Turnquist:
As the title says, Greg is the head of the Spring Data JPA project, and has written multiple books about Spring.
That was seriously fun. :) Greg is always great to talk to, and we got many more viewers than I expected. Of course, I only expected one (Hi Bill!), but several others showed up and made lots of comments. We talked about a huge range of topics, from Spring to JPA to how horrible the Wildfly logo is (they named their software after a bug??) and much more. I hope to do another live stream soon.
I hate it when I’m wrong…
… but that doesn’t keep it from happening. The worst is when I’m not only wrong, I’ve been teaching the wrong thing. That’s just awful. This week I found out I was wrong more than once, which was really, really annoying.
(As a quick aside, I’m assuming most of you at least can appreciate that feeling. This leaves out my wife, who in our 30+ years of marriage has yet to be wrong. That’s an extraordinary record. But I digress.)
As I mentioned in last week’s newsletter, Spring Boot released version 3.1 while I was recording an update of my Spring and Spring Boot course on the O’Reilly Learning Platform. The changes weren’t dramatic, but one of them provided an opportunity. If you check the release notes, one of the major new features is the support for the Testcontainers project via something called a @ServiceConnection
annotation.
I tried implementing it in one of my own tests and immediately ran into a problem.
That turned out to be a common theme this week. Three times during the week I tried to implement something I thought I knew, only to run into either a bug in a tool or — more dismaying — something I misunderstood for a long time. In the case of testcontainers, the story at least had a happy ending.
Problem 1: Hibernate doesn’t create-drop like I thought
I was trying to update my regular project to use testcontainers to supply a Postgres database. That’s one I don’t even have installed locally, so if it worked at all, it was going to work via testcontainers. Unfortunately, I kept hitting errors, mostly involving database tables not being found.
Eventually I did what I tried the last time I hit a testcontainer problem, which is visit their Slack channel. I went there and stated my problem, and a developer named Eddú Meléndez answered right away. In fact, we went back and forth, with me showing him my code in a GitHub repository, until we resolved the problem.
It turned out that I was relying on the database being re-created automatically, because the underlying Hibernate system was in what they call create-drop mode. That means Hibernate would drop the database when you shut down, and regenerate it on startup. As I said to Eddú, that’s its default behavior.
As it turns out, that’s not correct, or at least not always correct. It’s true when you’re dealing with an embedded database (like the default H2 database I always use when teaching), but not in any others, like the Postgres DB I was trying to use. For that one, you have to set the spring.jpa.hibernate.ddl-auto
property to create-drop explicitly in the application.properties
file, or it doesn’t work. As Eddú showed me, this fact is stated very clearly in Section 18.8.5 Configure JPA Properties of the Spring Boot documentation, which I never looked at because I thought I already understood it.
At least I can say I understand it now, and I’m very grateful to him for his help. Now I have the testcontainers demos working, so I was able to add a section to my course on them, and I’m expecting to record a new video for the Tales from the jar side YouTube channel on the topic this week.
Problem 2: Integration tests that rollback, don’t really
My other challenge this week came when I tried testing my controllers in the same application. I’ve made a big deal in my training courses over the years about how easy it is to do a full functional test on a Spring Boot application by running a @SpringBootTest
with the webEnvironment
property set to run on a random port. When you do that, Spring automatically starts up a test server for you, deploys your app to it, runs all the tests, and then shuts down the server. It’s really convenient.
The other issue is that when you add the @Transactional
annotation to a test, Spring starts a transaction at the beginning of each test, runs the test, and then rolls back all the changes at the end. That’s awesome, because it means you don’t have to worry about changes in one test affecting any of the others. I’ve always said that was one of Spring’s best features, and I’ve relied on it for years.
Imagine my surprise when I was recording a testing video for my course and I suddenly noticed a database change (inserting a new product) was surviving into the next test. That’s not supposed to happen. The problem is, it was happening, and (1) I couldn’t find anything I was doing wrong, and (2) I couldn’t stop it, robbing my course of one of its main highlights.
This time I didn’t have a good Slack channel to go to in order to ask a question. I couldn’t find anything on my problem online. I spent literally hours working on it, with no result. The longer I spent trying to find a solution, the more I had that awful feeling this wasn’t just some trivial configuration change — this was a fundamental misunderstanding that I’d been relying on, and teaching, for years. Ugh.
Eventually I took my very last option: I read the documentation. There, in Section 7.8.3 Testing Spring Boot Applications, I discovered this horrible paragraph:
In other words, the test server was running in a separate thread, so my rollback in my test (which I was doing correctly) didn’t prevent changes from happening on the server, or, more specifically, in the server’s database.
My immediate thought was, how could I not have noticed this before? I’ve been using a similar app in class for years, and the tests all pass. What the heck was going on?
I went back and looked at the tests on that older application, and this is what I saw:
Here’s the deal with that list: the tests that start with the word get don’t change the database. The one that says update does, but doesn’t affect the total number of elements.
The last five tests are the key. There’s an insert, which works, but it’s followed by a get that’s searching (deliberately) for a product not in the database, followed by two deletes, which empty the entire table, and a post that adds a product.
In other words, by sheer luck, none of the tests fail because any of the preceding ones changed the database. Even the deletes, which emptied the table, didn’t affect the final post, which just added a new product.
Incidentally, the ordering of the tests doesn’t match anything in the file. In one of my favorite phrases in any documentation ever, Section 2.10 Test Execution Order says:
By default, test classes and methods will be ordered using an algorithm that is deterministic but intentionally nonobvious [emphasis added]. This ensures that subsequent runs of a test suite execute test classes and test methods in the same order, thereby allowing for repeatable builds.
I love that: deterministic, meaning the order is the same every time, but intentionally not obvious, meaning they’re not going to tell you what the actual ordering is because they don’t want you to rely on it.
The bottom line is, I just got lucky. If the deletes on my old app had happened anywhere earlier in the process, I was doomed.
Here’s the list of tests in my new application:
Note they’re all passing, because I’ve fixed the problem, but see how the deleteAllProducts test appears in the middle? If that empties the product table, the next test fails immediately. When I first ran the app, before I fixed it, that freakin’ delete happened first, and down they went.
In other words, the order of the tests in my original app kept me from noticing that half the tests were indeed changing the database, and those changes weren’t being rolled back even though the tests themselves were doing so, just like the documentation said they would.
The fix is to run with a mock environment instead. That means using the regular @SpringBootTest
annotation with no parameters, and add @AutoConfigureMockMvc
as well, and then I can use the exact same test and everything rolls back the way I wanted. As you can see, all the tests pass now.
As with the other problem, at least now I understand it. Arguably I shouldn’t be admitting to getting something so fundamental wrong, but this is, after all, tales from the jar side and I’m all about those stories. Heck, I’m planning a video on that, too, nicely undercutting my authority as an expert, but at least in an entertaining way that hopefully keeps others from making the same mistake.
Problem 3: The GraalVM native image fails
Finally, one of the big changes in Spring Boot 3.0 was the addition of support for the native image compiler from the Graal virtual machine, GraalVM. What that means is you could take your finished application and run the nativeCompile
task on it, and the result would be an executable for your target platform.
That kind of violates everything Java is all about, with its “write once, run anywhere” philosophy, but for some applications it’s totally worth it. In my simple applications, when I run the nativeCompile
task it takes a few minutes to run (which is rather a long time in my world), but when you start up the result, it start in about a tenth of a second. Basically, as soon as you hit the enter key, the app is started. It’s really sweet, and makes for a nice demo, which I’ve been doing for about a year now.
That is, until the one time I wanted it to work, which was at the end of one of my recordings for my updated course. The compile step on my completed app worked well enough, but every time I tried to run it, the app threw an exception about some missing class that shouldn’t have been missing.
I tried backing off from Spring Boot 3.1.0 to 3.0.7 and it worked, so I thought I’d just go with that. But I didn’t do the actual recording for a couple of days, while I struggled with the other issues described already. Then, when I went back to do the recording, the run failed on both 3.1.0 and 3.0.7. Aw, nutbunnies.
I struggled with that for a while to no avail. Eventually I remembered that my course had a second app that we used in the first couple of modules, so I went back to that one. Lo and behold, that one worked, so I used it in the recording. I still have no idea why the other one isn’t working, and I have no idea even whom to ask about it.
Oh well. As the song says: don’t be sad, cause Two Out Of Three Ain’t Bad.
Wow, a Meat Loaf reference. I haven’t thought about Mr. Loaf in years, and hopefully I won’t think about him again for a lot more.
Um, don’t do this…
… and if you do, don’t double-down when you’re caught.
Here is a gift article from the New York Times, whose headline says it all:
Here’s What Happens When Your Lawyer Uses ChatGPT
The subtitle is, “A lawyer representing a man who sued an airline relied on artificial intelligence to help prepare a court filing. It did not go well.”
Yeah, I’ll bet it didn’t. Let me explain.
Roberto Mata sued the airline Avianca. The airline’s lawyers filed to have the suit dismissed. Mata’s lawyers filed a brief citing several relevant cases. The problem is, those cases were extremely relevant, almost perfectly connected, and that’s not normal. That should have been a red flag right there.
The defense lawyers tried to check for the cited cases and couldn’t find them, so they told the judge, and neither could he. He therefore asked Mata’s lawyers to provide them. They stalled, claiming people were on vacation, which was already a bad sign. As you have probably already guessed by now, that’s because the cases didn’t exist. The lawyer, Stephen A. Schwartz, used ChatGPT for relevant cases, and the AI tool simply made them up.
This behavior is pretty common among the major AI tools, and is technically known as “hallucinating.” Even the tool tells you to verify that the advice given may or may not be real. But Schwartz didn’t do that, and claimed not to know it was possible.
If Mata’s lawyers had at least admitted their mistake at that point, the judge might (might) have decided to be lenient. But no, another lawyer in the same firm insisted he had “no reason to doubt the veracity” of his colleague’s work. That house of cards didn’t survive even slight scrutiny.
I first heard about this from this Mastodon thread posted by Kendra Albert. It goes into much more detail. Here’s a post from the middle of the thread:
Here’s a later one:
In the end, the judge has ordered the plaintiff’s lawyers to show why they and their law firm shouldn’t be sanctioned, and why their filing of notarized (!) documents wasn’t fraudulent. Kendra finished with this:
Anyway, check out the whole thread for the juicy details. And don’t trust AI tools, especially when they tell you exactly what you want to hear. That’s what they’re trained to do, regardless of how connected the answers are to reality.
Tweets and Toots
Bacon
That might work. If not, at least you’ll have bacon.
Something Smells Funny
I’ll never be able to look at snowmen again without thinking about this.
AI vs Humanity, redux
Yup.
If you’re like me…
… you’ll read this twice, once to figure it out, and once in rhythm.
It scans perfectly.
Others make mistakes too
Apparently I’m not the only one who made mistakes this week, but at least mine didn’t involve communicable diseases, as far as you know.
But does it use ChatGPT?
Another step on the path to Generalized AI.
Polling AI choices
I asked ChatGPT for potential titles for my upcoming video describing one of the mistakes above and added some of the options to both a YouTube Poll and a LinkedIn poll. The one on LinkedIn has gotten 19 responses so far, while the YouTube poll has only gotten one.
So much for expanding the reach of my YouTube channel. Oh well. If you get a chance, feel free to vote.
The best comment, by far, was by Dan Hinojosa (Hi Dan!), who suggested the title should be “Rollback to where you once belong”. Get back, Loretta, indeed. :)
Calque vs Loanword
A Good Use of AI
AI can be used for good, as in this example:
Pirate, line officer, or privateer? Definitely a pirate. To fit that costume, I imagine he had to make it sew. (rimshot)
Doc Brown was creepy
And finally, I already know I’m old, but maybe some of you need this reminder of your own age:
Of course, Doc Brown didn’t accompany Marty back to 1955, but the point is made.
Have a great week everybody!
The video version of this newsletter will be on the Tales from the jar side YouTube channel tomorrow. Last week I also published each section as a separate, shorter video. The individual sections didn’t get a lot of views, but I’ll try it again this week and see what happens.
As a reminder, you can see all my upcoming training courses on the O’Reilly Learning Platform here and all the upcoming NFJS Virtual Workshops here.
Last week:
No classes.
This week:
Functional Programming In Java, on the O’Reilly Learning Platform (in the APAC time zones)
Week 1 of my Spring and Spring Boot in 3 Weeks course on the O’Reilly Learning Platform