When I was a kid, we lived at the end of a long gravel road without any neighboring kids we could play with. I was the youngest, with a sister four years older and a brother two beyond her. To while away lazy, pre-Internet summers when we’d read all our books and the rabbits were fed and the clouds and the neighbor’s cows doing nothing of interest, we’d often play a game of our own invention called “Roller-Bat”.
Today, for the first time in a long time, I tried out a new product I was genuinely excited to get hold of.
Capitalism is not, as many millennials think, the root of all evil. Neither, as many boomers seem to think, is it the garden of all virtue. There is a balance to be found between public and private interests, and between innovation and foolish obfuscation. The shaving business is a case in point.
If you’re under 40 and don’t have an MBA, you may not be aware that the shaving razor business is a scam so well known it’s part of the Harvard curriculum. It works like this. Give away an attractive razor for cheap or for free, then make a profit selling the owner proprietary replacement cartridge blades that you somehow convince them are better in some way than the crazy cheap standard blades they were using before. Bonus dollars if you hook them young enough they never used the cheaper alternative, or still think of it as grampa’s old school. Why buy 50 blades for $9 when you can buy two for $10 and get half the performance? Ah, but the packaging is so manly and sleek, like what Captain Kirk would get his condoms in.
It’s more than disappointing that as I write this in 2022, more than a generation since I first worked as a software developer, nearly a century since the first programmable electronic computers, we are still wrestling with things as archane and crude as build files and what the Java community not-so-affectionately calls “dependency hell.”
For the uninitiated, computer programs are written in relatively “high-level” human-readable languages like Java and C, but execute in a low level machine language understandable by a microprocessor. In simple terms, the process of getting from high-level code to executable code is the “build,” and build files are instructions to help govern that process. Dependency hell arises because Java programs are built up from libraries of pre-built components that do everything from manipulating strings to writing the contents of memory out to an application file (like the .docx file produced by Microsoft Word). No developer or team could possibly afford the time or money to implement all these supporting functions from scratch, and libraries provide standardized mechanisms for various entities to develop and support them and make them available to application developers. So far, so good.
The trouble is, over time, code gets written against specific versions of these libraries, and sometimes becomes unintentionally dependent on version-specific bugs or peculiarities. Library ownership changes hands and the new owners decide to clean house, reorganize, or kill existing libraries. Open-source libraries branch into new flavors and those that were once a defacto standard become tomorrow’s orphan, while the new alternatives may be as similar as two sects of the Greek Orthodox Church or as different as Chinese and English. Meanwhile, applications are always being written and maintained, building up every growing and changing rat’s nests of dependency on these various libraries.
This is not unique to Java. .Net developers face “DLL hell,” but .Net developers are far more dependent on components written and maintained by Microsoft. Java is somewhat more vulnerable to it because its greatest strength–an open, platform-independent architecture–is inherently dynamic and fungible. Unfortunately, the language’s original developers didn’t foresee and/or adequately address this issue, so today we face the unenviable situation in which many existing applications cannot, for all practical purposes, be updated to the latest greatest (and more secure and supportable) libraries and language core because the change would simply be too expensive. The best solution would have been to build right into the language some mechanism for guaranteeing backward compatibility to all libraries, forever. Then the compiler could simply choose the latest version of every library it could find and it would be guaranteed to work so long as the application didn’t require functionality newer than the available version. Even that could be addressed by a centralized repository and automated system to permit the compiler to go find what it needs. Alas, that seems reasonable today (though it would be more problematic than it sounds) but it was impossible when Java first appeared.
Still, changes have been gradually made to improve the situation. One of these is that at a certain point, a rule was instituted that dependencies had to be unambiguously specified. Throughout the first 8 major releases of Java, this was not the case. You could reference two libraries, one of which had been compiled with the other, and the two different versions of that other would be equally available to the compiler and runtime. Not only would this make your application code larger, it meant that which library was used at runtime was a bit of a crap shoot. Mechanisms were in place to let you take control, but in the vast majority of cases, developers ignored them except when testing found a specific problem.
Do you know what programs that appear correct because of testing are called? Miracle programs. In theory at least, programs should be provably correct, like mathematical proofs. That’s not often the case in practice, but at the very least, we should avoid practices that necessarily increase the odds of hidden defects. Allowing random resolution of multiple library versions is one such practice, so starting in Java 9, it’s no longer permitted.
This brings me here today. I’m working on a novel design application derived from an open-source Java project originally developed in Java version 7. It contains lots and lots and lots of these “transitive dependencies” as they are called. When I try to compile it under Java 9 or higher (16 is the current version) I get almost 300 errors stating “Package such-and-such is available from more than one module…” This wouldn’t be so bad if the IDE (Eclipse) or build tool (Gradle) contained a nice little hook to let you right-click, resolve the problem and move on–you know, the way normal people would do it. But this is Java, so normal people need not apply. In fairness, it’s not JUST that the Java and open-source communities are arrogant twits who see no problem in requiring others to master arcane technical skills to accomplish simple, obvious tasks that should be baked into the interface. It’s also that very often, they also lack the resources or authority needed to bake-in such features, and that’s why we are stuck in a world dominated by Microsoft. But I digress…
MAKE A COMPLETE BACKUP OF YOUR ENTIRE PROJECT DIRECTORY BEFORE BEGINNING.
KEEP A RECORD OF EACH CHANGE YOU MAKE. YOU WON’T BE ABLE TO COMPILE AND TEST UNTIL ALL ERRORS HAVE BEEN REMOVED, SO THERE’S A POSSIBILITY OF RUNTIME ERRORS CAUSED BY CHANGES MADE TO REMOVE COMPILER ERRORS.
DO NOT TRUST SUDDEN LARGE REDUCTIONS IN ERROR COUNT. THIS MOST LIKELY MEANS ECLIPSE HAS REVERTED TO A LOWER COMPLIANCE LEVEL AND MAY BE LYING ABOUT IT. CHECK THE COMPLIANCE LEVEL. PERFORM GRADLE AND ECLIPSE CLEANS. REVERT LAST CHANGE. RESTART IF NECESSARY.
REMEMBER TO MANUALLY SAVE CHANGES TO THE BUILD FILE. FORGETTING WILL LEAD YOU ON A MERRY CHASE WHEN ERRORS DON’T DISAPPEAR WHEN YOU EXPECT THEM TO.
So here’s how to resolve the problem in Eclipse and Gradle:
- Go to the command line and change to the folder containing your application. You can grab this in Eclipse by opening Project/Properties/Resource/Location.
- Run the command “gradle -q dependencies”
- Copy the resulting report and paste it into a searchable editor (like Notepad++)
- In Eclipse, go to Project/Properties/Java Compiler and change the compliance level to Java 9. It’s probably best to leave the workspace settings alone for now, as you’ll want to switch back and forth a lot to make sure you haven’t broken anything as you proceed. At least in my version of Eclipse, the compliance level gets set back to Java 1.7 (version 7) after almost every build, for which I thus far have no explanation. Change it to 9 (or higher), apply and close, and the project will build with Java 9 compliance. You’ll see a large number of “such-and-such cannot be resolved to a type” error caused by the failure of library import statements that no longer work due to transitive (or other ambiguous) dependencies.
- Double-click on any of these errors, and a related source code (Java) file will open. Go to the top, then scroll down until you see imports that have been flagged with the message “Package such-and-such is available from more than one module…”
- If you see an import ending with a wildcard, skip it for now, find one that’s a specific type.
- Highlight the type and press control-shif-T to open the type viewer (in Eclipse). You should expect to see multiple .jar files listed. The type viewer tells you exactly where the file is, but that’s not actually helpful. Instead, you need the name of the library (group or module), usually indicated in the filename of the jar. We are going to try to eliminate from this list all but one of the dependencies, preferably one with (jre) beside it because that’s a supported part of the Java language itself.
- First, search your gradle.build file–the one written in Groovy script and containing at least a “dependencies” section and multiple “implementation” lines.
- If one of the multiple depencies is referenced here, you can try just deleting it (comment it out though, so you can bring it back if needed).
- Save the build file and use Project/Clean to clean and rebuild. If all errors dissappear, it probably reverted the compliance level to pre Java 9. Use Project/properties/Java Compiler to change it back to 9 or higher, apply and save, and see what that does.
- If the total number of errors has gone down, that’s a good sign, but even if it hasn’t, you can go back and look at the same type in the type viewer and you should see one fewer dependencies. Repeat until there is only one and the error count is down by at least one. Fortunately, fixing this issue anywhere fixes it everywhere, so the error count can go down by tens or more all at once.
- IF THE ERROR COUNT GOES UP, comment the dependency back into the build file and try something else. The idea is to find a library that contains a redundant reference to another, usually more generic library, and either eliminate it (the library containing the reference) or add an exclusion removing either the entire redundant dependency or better yet, the specific redundant dependency added by that library. Put another way, if your application requires library “A” and library “B” but is failing because “A” contains “B” and Java already contains “B”, you need to tell the runtime and build tools to ignore the version of “B” contained inside “A”.
- Search the dependency report you copied earlier for references to the problematic library. This will give you clues as to which listed dependency may be including a redundant (transitive) reference.
- If you can omit a dependency that includes the problematic transitive reference and get away with it, great.
- If you can’t, you might be able to add an exclusion at the bottom of the build file something like this one for xml-apis (though I haven’t worked out the details of how best to use this, I found it following an online thread):
all*.exclude group: ‘xml-apis’
- Or better yet, you can specifically exclude the transitive dependency brought along by some other needed library. That library will still use the code it brought with it, but the compiler and runtime won’t see it on the classpath and get confused.
// Apache Batik
// implementation ‘org.apache.xmlgraphics:batik-dom:1.8’ // CSH exclude added for Java 1.8 to 9 update
exclude group: ‘org.apache.xmlgraphics’, module:’batik-ext’
Today I made a special trip to the big-box hardware store only to learn they no longer stock aluminum HVAC tape. In case you don’t know, this product is the jack-of-all-trades “duct tape” (more properly Duck Tape) pretends to for a wide range of tasks around the house, studio, and attic. It’s strong, easily to apply, light-proof, leakproof, and removable, almost always without a trace. And unlike Duck tape–which was designed for waterproofing ammunition crates in WWII, not for sealing ducts, aluminum HVAC tape sticks like hell, even to less than perfectly clean surfaces, is impervious to moisture and heat, and will not turn to dust in the attic.
I have used it to build studio lights, seal leaks in my HVAC system, build an attic door insulation cover out of scrap polystyrene, attach dryer vent hoses, and as a removable duct attachment to permit annual cleaning of the dryer exhaust duct through the attic. When I renovate, I also use it the seal the duct plenums to the ceiling drywall to reduce drafts and prevent windy weather from pushing attic dust down into the house.
Recently, I installed an air return from our master suite, which forms a peninsula at one end of the house and which has always been difficult to heat. Upgraded windows, attic insulation, and a duct booster fan have helped. The air return is to provide more space for air returning to the HVAC intake without having to squeeze around the bedroom door, building up a rind of dust over time.
I grew up at the tail end of the Cold War. I’m a nerd, and I used to play “camping” under giant vinyl sheets my dad brought home from the Air Force which I later realized were maps of aerial bombardment exercise ranges in central California. My dad’s job was to fly off on a moment’s notice never to return and convert thousands of Russian children very much like me into radioactive corpses. We didn’t worry too much, as we understood the point was that no rational person would start such a war knowing how it must inevitably end.
While prepping for a recent “Pay it Forward Day” presentation, I stumbled on a useful analogy: submitting creative work for sale is a bit like dating.
It’s tempting when a submission doesn’t work out, to think something like a teenager who just got ghosted by the hottest kid in the “in crowd”, to think “I wish I knew why they didn’t like me, then I could change.” You might even be tempted to respond to a purchasing editor asking exactly that. Don’t. Don’t, because you are selling yourself short. Don’t because you’ll look like a pestering kid asking “but why don’t you like me? Why? Why?”
You may have noticed, there’d a war on. Of course, that’s 6,000 miles away, so it doesn’t affect me, right? I can turn a blind eye the way most people do, most of the time when atrocities and injustices are happening a quarter of the circumference of the planet away, right?
Well no, not so much. If you are reading this on my blog, there’s a small but non-zero chance you’ve noticed my website was down for over 30 hours and has been running with all but the home page broken for several days. Indirectly, this is because of that war–my hosting provider no longer has contact with the region of the world from which, for many years, it’s been getting exceptional support labor. As I write this, Vladimir Putin is in the process of walling Russia off from the global Internet so his subjects can’t be told the truth. President Biden has barred all energy imports from Russia and many other nations are likely to follow suit, though this is mostly symbolic since shipping in the Black Sea essentially stopped the day Lloyd’s of London declared it a war zone. International sanctions mean that all Russian airlines are now required to return half their airplanes, all but 170 something of which are leased from Western companies for efficiency. And they’ll find a way to do it, too unless Putin puts a gun to their heads. They want this madness to end so they can go back to making money, and to compete in the global marketplace, they need more than permission to fly outside Russia, they need those leasing agents and access to tech support in the US and maintenance facilities in Germany.
Meanwhile, Ukraine isn’t just firing tank and aircraft killing missiles from the US and Western Europe–it’s firing its own, made with parts designed by a design company in Belarus–one brown shirt national thug nation supplying a goodly portion of Putin’s army of pointless conquest.
Putin claims he can’t abide NATO expansion toward Russia’s borders. This is absurd, in part because NATO was created to protect its members from a USSR hell bent of ideological colonization of the planet and in part because since the fall of that empire, the expansion of the alliance has been driven not by the US or other Western powers but by the well-founded fear of Russia’s neighbors. Russia has nothing to fear from NATO if it stops invading other countries, and those countries have no reason to join NATO unless they fear Russian beligerance.
Meanwhile, NATO or no NATO, economic ties binding Russia, the West, and all the former Soviet satellites had done more to ensure Russian security in the last 20 years than all the wars and “special military operations” fought by Putin or his predassesors all the way back to the iron age–until he pissed all that away in a week.
The best defense against attack is close economic partnership. Best, because it both fosters the communication and partnership needed to work out disputes without recourse to violence and greatly lessens the need for spending on defense.
The people of Russia, Ukrain, Belarus, and for that matter, Finland and Texas, have no interest in empires. They just want to raise their kids, see if Elon Musk really does die on Mars, and get prompt support for their website when they need it.
Wars of conquest are stupid. They serve nothing and no one but the egos of the men who start them. Thus, those should be the first to die when the shooting starts.
More people should be using LibreOffice. Why? Because word processing is a mature technology, as mature as the doorknob, and there is no reason to keep paying tech companies like Microsoft increasingly exorbitant protection money as if we needed or wanted them to “improve” it–all the while making it easier for them to spy on us and sell us crap we don’t want. Capitalism is a powerful driver of progress, but sometimes that progress is, as C.S. Lewis put it, called “going bad.”
This isn’t 1988. Word processing doesn’t need anything but bug fixes and refinement between now and whenever the next revolution in AI or human biology renders the whole idea moot, and well-supported open-source software is safer than commercial software specifically because it’s open. More eyes are looking at the code with more detachment. It’s also a bit like a good Credit Union, driven by the needs of the community rather than the profits of a few oligarchs. So stop paying that monthly subscription and download LibreOffice for free. Go. We’ll wait.
But when you do, you’ll naturally find there’s a learning curve. That’s where I can help. I’m starting a series to share what I’ve learned as I’ve made the transition over the last decade or so.
In this post, the Libreoffice Macro facility and how to easily use it to do something super useful with no difficulty at all: Make Libreoffice Write open the last document you edited to the last spot you were at when you closed it.
The idea that Telsa worked on some anti-gravity technology now lost is a myth. He also didn’t invent the three-phase electrical distribution he’s so often credited for. That was Mikhail Doliva Dobrovolsky, a Russian engineer working in Germany, who was the first to demonstrate polyphase power when he used it to transmit hydroelectric power many kilometers to power an 1891 exhibition in Frankfurt am Main, in Germany.
At around the same time, Tesla patented a polyphase motor, which he later agreed was identical to Dobrovolsky’s, who did not patent his discoveries out of a (perhaps misguided) sense of civil duty.
Tesla, though, was working for Westinghouse, who was keen to hold him up as the great genius behind the new electric age, while Dobrovolsky, a Russian working in Germany at a time of growing tensions between the two, was championed by no one. So the myth of Tesla grew in the American press, and today millions think of him as a cult-like figure, the great American misunderstood lone inventor when the reality is very different.
Tesla was a great inventor and engineer and contributed mightily to the early 20th-century explosion of electrical infrastructure, but he was one of many, many such contributors, and he was rather less successful in his pure scientific pursuits, where his focus on practical experiment and lack of theoretical grounding led him to waste energy on all sorts of unworkable nonsense like through-the-air power transmission and death rays that even at the time could have been shown to be impossible along the lines he envisioned.
Edison was not the lone wolf who invented the light bulb, nor Bell the telephone, nor Tesla polyphase power. And while Tesla never worked on anti-gravity as far as we know, he did apparently spend much of his adult life tinkering on his own “Dynamic Theory of Gravity,” which in a letter written at 81 years of age, he claimed would “put an end to idle speculations and false conceptions, as that of curved space” and he said it was “worked out in all details” and he would “soon give it to the world.”
He was claiming to have refuted Einstein, which would have been quite an achievement, but Einstein has been experimentally confirmed to precisions Tesla could not have dreamt of, and Tesla’s theory, if it ever truly existed, managed to vanish utterly from his papers.
I say all this not to denigrate Tesla or his contributions, but to point out that history tends to lionize the most visible faces behind significant events and gloss over the many others who invariably make the events possible, the societal influences and interchanges behind them, and indeed, the substance of the contributors.
Tesla also said,
man’s new sense of pity began to interfere with the ruthless workings of nature. The only method compatible with our notions of civilization and the race is to prevent the breeding of the unfit by sterilization and the deliberate guidance of the mating instinct … The trend of opinion among eugenists is that we must make marriage more difficult. Certainly no one who is not a desirable parent should be permitted to produce progeny. A century from now it will no more occur to a normal person to mate with a person eugenically unfit than to marry a habitual criminal.
If this statement were your only guide to the man’s character, you might well judge him with Hitler. But he also spoke out in favor of women’s equality, and during the second world war, sought to develop a death ray in support of the allies.
Tesla was not a God-like genius, stifled by a society unprepared to accept his magical inventions. He was a human being born of his times and situation, as are we all, and to buy too deeply into his myth betrays not only the memory of the lesser-knowns, but the very pursuit of truth to which he seems to have been genuinely committed.
This is good design:
This is poor design: