An Emacs Indentation setup for Java

Categories
Emacs logo + Java Duke mascot logo

tl;dr - I had to use a slightly modified indentation setup for java-mode to get close to the IntelliJ Java indentation scheme while doing some Java development. I also had to run Maven (mvn) lots from the command line so I’ve included some tips on that. If you hate unnecessary rambling, jump to the elisp code

Generally, working with the Java programming language is not my first choice, but sometimes when money is involved I absolutely ignore my first choices. Recently while working with a rather large, corporate client’s codebase which was (as you might have guessed) in Java, I needed to find a way to use my beloved [Emacs]emacs with the project I was working on. This post is a bit of a mishmash of the things I found I needed to change to get some reasonable looking auto-indented code to work.

Unfortunately, this is not a guide on how to use Emacs to write & debug java as easily as you might in something like IntelliJ with a tool like The Java Development Environment for Emacs, mostly because I didn’t bother with it. I did my building & running from the commandline, using Maven (mvn) painfully manually (and with some git hooks) from the command line. I will, however, leave some tips on how to run mvn painfully from the command line (even though you really shouldn’t).

This also won’t be a diatribe on why I eschew IDEs and/or Java, let’s keep things productive. One more time for clarity – you should absolutely probably use an IDE, in previous positions I’ve even run emacs and IntelliJ side by side, popping into IntelliJ whenever I needed to perform builds. There are also likely better ways to integrate Emacs and Java that I’m not going to cover at all. If you’re going to ignore all those warnings then continue on. A quick Q&A with myself before we get started:

Q: Why not just use emacs bindings in IDEA?

A: Emacs bindings != Emacs, there’s a whole ecosystem to replicate.

Q: Why not just run a terminal inside IDEA and run Emacs in there

A: This is a reasonable solution (I’ve done this in previous roles), but IIRC there’s some overlap keyboard shortcuts and it’s a bit unwieldly.

Q: But how will you get your jump-to-definition and X feature?

A: I won’t – easy find in file & find in project are good enough for me.

To IntelliJ/the community’s credit they’ve made it really easy for outside tools to work, so there are one or two packages that help with this:

None of these really worked for me as a solution (and I failed to look into the others) – I basically chose to stick with running mvn in the terminal, and pairing it with tools like watch or entr as necessary.

The Emacs LISP code

Here’s the emacs code that works to get some decent (I updated this as I found things that didn’t match properly):

;; Java
;; use C-c C-o to set offset
;; use C-c C-s to show syntactic information (show the variable that needs to be set)
(add-hook 'java-mode-hook (lambda ()
                            (setq c-default-style "java")
                            (c-set-offset 'arglist-intro '+)
                            (c-set-offset 'arglist-close '0)
                            (c-set-offset 'case-label '+)
                            (display-line-numbers-mode 1)
                            (auto-complete-mode t)
                            ))

You might be wondering why I have those notes there about using C-c C-o to set offsets and C-c C-s to show syntactic information – well they’re there because I used those two commands enough to try and figure out what looked good that I never wanted to forget again.

Using Maven from the command line, painfully

As mentioned earlier, you probably shouldn’t ever do this, but I did. By avoiding IntelliJ and JDEE (Java Development Environment for Emacs), I went the likely seldom traveled path of just running mvn as necessary while doing my development. If you’re not at all familiar with mvn, you might want to start with the 5 minute guide to maven, and then maybe read the 30 minute guide, AKA the getting started guide.

Maven has been around a very long time, and while it’s arguably managed it’s growth well, it’s pretty difficult to use intuitively from the command line. Never mind the way the configuration is laid out, and the concepts behind Maven and how they manifest in it’s usage and configuration, I’m going to leave it at the fact that there is almost no one who has ever remarked at how easy Maven was to get started with. Whether it’s the plugin system, the goals/phases, the XML soup, restrictive/prescribed file layout, and how all these pieces fit together, Maven is a fucking jungle. People are only happy with maven once they’ve spent years in that jungle. Again, to be fair, Maven is very old – comparing it to modern build systems is unfair, so I won’t.

Anyway, here’s wonderwall here are a few command lines that spent a lot of time in my history.

The basics

The absolute basics – best run from the root of your project (topmost folder with a pom.xml):

$ mvn install

Build, package, install the package. This command does everything necessary to put a built JAR in your ~/.m2 (as specified by the M2_ROOT environment variable).

$ mvn compile

This just compiles the project in the current directory – quicker than a full install and a good way to find out if any of the monstrosity that the code you just wrote actually works.

Maven has a bunch of command line options, and most of the following is going to be us going through them.

Restricting to a certain subproject

Most maven projects are multi-module, so you normally have a folder structure like this:

your-enterprise-project/
└── moduleA
├── moduleB
│   └── src
        ├── main
        └── resources
│   ├── test
│   └── ... other folders ...
├── pom.xml
├── ... other files ...

After writing some code that affects moduleA, you probably want to avoid building moduleB just to check if your code compiles. What you can run instead of a regular mvn compile:

$ mvn -pl=moduleA compile

The -pl (--projects) argument actually restricts the projects that are allowed to run, and you can use that to limit the running projects/modules to moduleA. What about if moduleA depends on moduleD (the D is for Dependency)? We’ll you’ll need to run:

$ mvn  -pl=moduleA -am compile

The addition of -am (--also-makeIf) instructs maven to build the dependencies of the projects that are being built.

Cleaning

You’re going to want to clean very often, if anything to avoid weird builds. You might immediately think to yourself “weird builds? what a vague way to put that” – and you’d be right, I’m being vague here because the amount of problems I ran into that just shouldn’t have happened due to running regular builds made me stop running compiles without clean. Some things were expected, like work with Avro where classes were generated at build time based on specifications. I ended up just appending clean for most things:

$ mvn clean compile

Or

$ mvn clean test-compile

Became my go-tos. I won’t include clean everywhere for the rest of this post, because theoretically mvn should work just as well when it doesn’t clean everything out, but that’s definitely not the case.

Running a single unit test

Here’s a thing that you’d think was relatively simple but you’d almost certainly have to google search to find out how to do – run a single unit test:

$ mvn test -Dtest=<TheClassYouWant>#<the method you want>

If you’re in moduleA/ (or have -pl=moduleA set in the project root), and you wanted to run a method named someTest in SomeClassUnitTest.java, you’d run:

$ mvn test -Dtest=SomeClassUnitTest#testSomething

And if you wanted to run the whole class (this time, we’ll pretend we’re in the project root):

$ mvn -pl=moduleA -am test -Dtest=SomeClassUnitTest

Running a single integration test

Turns out running a single integration test isn’t quite the same as running a single integration test. To run a single integration test, you’re going to need to use:

$ mvn -pl=moduleA -am integration-test -Dit.test=SomeClassIntTest#testSomethingsIntegration

Running a single main class in your project

Sometimes you just want to run Main.java/App.java from a given subproject without any fuss. For those times, use:

$ mvn -pl=<subproject> -am exec:java -Dmain.class="path.to.your.class.Here"

Running your Spring Boot app with a given profile

Spring Boot is the new (actually kinda old) hotness (where regular Spring Framework projis the old hotness) – it’s dominant in the landscape for Java applications in the enterprise. Sometimes you just want to run your spring boot application with maven, assuming you have the Spring Boot Maven Plugin installed – for that, use:

$ mvn spring-boot:run

And what about different spring profiles? Well the best way I found was to just use the SPRING_PROFILES_ACTIVE environment variable:

$ SPRING_PROFILES_ACTIVE=production mvn spring-boot:run

Running your Spring Boot enabled JAR with a given profile

Let’s say you mvn install install your way to a working JAR (which lands in the target directory) what about when you want to run that JAR, with some overrides here and there?

$ java -jar /path/to/your/project.jar --spring.profiles.active=production --other.options=str --another.option=1

An issue I ran into with auto-complete & yasnippets

I use yasnippet and auto-complete, and for the longest time, my java development was less productive than normally afforded by these plugins due to an error about a void function definition being missing for the current-snippet-table.

While this isn’t very good content, and pretty anti-climactic I fixed the issue I ran into by re-installing yasnippet and auto-complete, and disabled some custom setup code I had in ~/.emacs. Maybe this section shouldn’t even be here, but if you’re not using yasnippet and auto-complete you should start – it’s very useful, especially in Java land.

Wrapup

Thanks for stopping by – hopefully you enjoyed this post more than I enjoy writing Java code.

Did you find this read beneficial? Send me questions/comments/clarifciations.
Want my expertise on your team/project? Send me interesting opportunities!