To move classes within a jar from the root (unnamed, default) package into a named package (e.g. org.example
):
1. Rebuild the Jar from source
Using your IDE, move the classes out of the root/unnamed/default package into a proper named package, and build the jar again.
Simply unzipping the jar and moving the class files around does not help - the class files have to be modified internally, which is what the tools below do.
If you don't have access to the sources, or if you don't want to mess with the source code - there are a couple of solutions I found inspired from this related discussion.
2. Jar Jar Links tool
https://github.com/shevek/jarjar
Its old but still works. Get it from here as discussed here.
The way I used it
# View help
java -jar "bin/jarjar-command-1.0.3.jar" --help
# Run the command
java -jar "jarjar-command-1.0.3.jar" --mode process --rules "rules.txt" target/my.jar --output target/my_new.jar
# View contents of resulting jar
jar tf target/my_new.jar |less
The rules.txt
to match root classes is *
rule * com.example.@1
so the rule moves them into the com.example
package. The @1
means the thing that was matched.
Unfortunately it doesn't seem to work on classnames involving the $
character - see this issue.
3. Use the Maven Shade Plugin Tool
https://maven.apache.org/plugins/maven-shade-plugin/usage.html
This worked better for my case. Add this fragment to your pom.xml
inside the <project>/<build>/<plugins>
tag.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<relocations>
<relocation>
<pattern></pattern>
<shadedPattern>org.example.</shadedPattern>
<includes>
<include>TypeScriptParser*</include>
<include>TypeScriptLexer*</include>
</includes>
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
To match the classes in the root of the package, I used <pattern></pattern>
though there may be a better way.
Also, for my use all my root classes started with TypeScriptParser*
or TypeScriptLexer*
so I included those explicitly. It took a bit of trial and error to get working on the right classes.
Running this on my build with mvn package
did introduce extra classes from my project into the jar that I didn't need, so I had to delete them manually (by unzipping/zipping using jar)
Discussion
Knowing that java files in named packages cannot access classes in the unnamed root package (whether they be in your project or in a jar) was not obvious to me, but was finally explained to me here. I wonder how many Java developers get tripped up by this.