The Alternate Java Library Guide: Libraries You Haven’t Heard Of But You Should — part two

This is a second part of the Java useful libraries list, you can find first one here:
What do I consider a library that you should try?
In various Java libraries, some shine brightly and have become an industry standard — think of pillars like Apache Commons, Google’s Guava, or JUnit. Then, some libraries are “lesser-known”, and though they may not headline every developer conference or star on GitHub’s trending page, they have the potential to be just as valuable in the right circumstances.
What makes a library “lesser-known”? It isn’t simply a matter of raw download statistics or GitHub stars. A lesser-known library may be pretty popular within niche circles but hasn’t yet broken into the mainstream consciousness of the average Java developer. Perhaps it’s a new library that’s still gathering momentum, or maybe it serves a particular purpose that not every developer needs — but those who do cherish it deeply.
To choose the libraries I’ll discuss in this article, I considered the following criteria:
- Community Support: While the library may have little recognition, it has a dedicated user base or a small, active community contributing to its maintenance and evolution. This ensures that you are covered with resources when you adopt.
- Documentation Quality: For a library to be recommended, it must have enough documentation to allow a beginner to install it and use its essential features.
- Unique Functionality: There’s something special about each library selected that fills a gap in the Java ecosystem. This could be an innovative approach to a common problem or a niche functionality that’s hard to find elsewhere.
Networking and Integration
In Java development, efficiently handling network operations and integrating diverse systems are critical for building robust applications. Apache Mina and Apache Camel are two powerful yet lesser-known libraries in this domain. While Mina is used for simplifying network application development, Camel is a versatile integration framework.
Apache Mina — Streamlining Network Application Development
Apache Mina is a network application framework designed to facilitate the development of high-performance and scalable network applications. It abstracts the complexities of network communication, allowing developers to focus on application logic.
Here’s how you can set up a simple server with Apache Mina:
// Import necessary Mina classes
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import java.io.IOException;
import java.net.InetSocketAddress;
// Extend IoHandlerAdapter to handle events
class MyServerHandler extends IoHandlerAdapter {
@Override
public void messageReceived(IoSession session, Object message) {
// Logic for handling received messages
System.out.println("Message received: " + message);
}
}
public class MinaServer {
public static void main(String[] args) throws IOException {
// Create a NioSocketAcceptor
NioSocketAcceptor acceptor = new NioSocketAcceptor();
// Set the handler that will handle events like messageReceived
acceptor.setHandler(new MyServerHandler());
// Bind to a port to start listening
acceptor.bind(new InetSocketAddress(12345));
System.out.println("Server started on port 12345");
}
}In this example, MyServerHandler handles events like receiving messages. When a message is received, it simply prints it out. The server listens on port 12345.
Apache Camel Simplifying Data Integration
Apache Camel is an integration framework that facilitates the interaction between different systems, managing data transformation and routing. It supports numerous transport protocols and data formats, making it a versatile choice for integration solutions.
Here’s a basic example of using Apache Camel to route data from one endpoint to another:
// Import Camel classes
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
public class SimpleCamelRouter {
public static void main(String[] args) throws Exception {
// Create and start a CamelContext
CamelContext context = new DefaultCamelContext();
context.start();
// Add routing rules
context.addRoutes(new RouteBuilder() {
@Override
public void configure() {
// Define a simple route
from("file:inputFolder").to("file:outputFolder");
}
});
// Keep the application running for a while
Thread.sleep(10000);
// Stop the CamelContext
context.stop();
}
}In this snippet, Camel is set up to monitor an inputFolder directory and automatically transfer any files placed there to an outputFolder. The route is defined declaratively, showcasing Camel’s ease of use for integration tasks.
Testing and Quality Assurance
Tetsting is always a controlversial questions for all developers. Writing a test is always a challenge and in some cases, it is even harder than just writing a code. Two libraries that significantly enhance your experience are Testcontainers and Arquillian. They offer unique approaches to testing, especially in environments closely resembling production setups.
Testcontainers: Simplifying Integration Tests with Docker
Testcontainers is a Java library that supports the use of Docker containers in JUnit tests. This is particularly useful for integration tests where you need to test against a real database, web server, or any other external dependency. Testcontainers manage the lifecycle of these containers, ensuring they are available during the test and cleaned up afterward.
Here’s how you can use Testcontainers to test against a real PostgreSQL database:
First, include Testcontainers in your project’s dependencies. For Maven, add:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.15.3</version>
<scope>test</scope>
</dependency>import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class PostgresTest {
// Create a PostgreSQL container instance
private static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:11.1");
@Test
void testSimple() throws Exception {
// Start the container
postgres.start();
// Now you can use the container in your tests
String jdbcUrl = postgres.getJdbcUrl();
// …create a connection to the database, run some queries, etc.
// Check if PostgreSQL is running
assertTrue(postgres.isRunning());
// Stop the container
postgres.stop();
}
}This test starts a PostgreSQL container, which can be interacted with as if it were a regular database host.
Arquillian: Streamlined Testing for Java Middleware
Arquillian is a testing platform designed for the JVM, enabling developers to easily create automated integration, functional, and acceptance tests for Java middleware. It brings the test to the runtime, thus minimizing the differences between the test environment and the production environment.
Here’s a simple example of an Arquillian test:
First, add Arquillian to your project dependencies. For Maven:
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<version>1.7.0.Alpha9</version>
<scope>test</scope>
</dependency>Then, write a test class:
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertNotNull;
@RunWith(Arquillian.class)
public class MyArquillianTest {
@Deployment
public static JavaArchive createDeployment() {
return ShrinkWrap.create(JavaArchive.class)
.addClass(MyClassToTest.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
@Test
public void testMethod() {
// Test logic here
MyClassToTest myObject = new MyClassToTest();
assertNotNull(myObject);
}
}In this example, MyArquillianTest uses Arquillian to deploy MyClassToTest in a real container environment, enabling you to test it as it would behave in production.
These tools help ensure that applications are not only well-tested but are tested in a way that faithfully represents their real-world operation.
Domain-Specific Libraries
Java is not just about general-purpose programming. It also offers specialized libraries for specific domains. Two such libraries, BioJava and Smile, cater to the fields of biology and artificial intelligence, respectively.
BioJava: Processing Biological Data
BioJava is a toolkit designed for the manipulation, analysis, and visualization of biological data. It is commonly used in bioinformatics, providing support for various tasks such as sequence analysis, protein structure, and genomics.
Here’s a basic example of how BioJava can be used to read and manipulate DNA sequences:
First, include BioJava in your project’s dependencies. For Maven, add:
<dependency>
<groupId>org.biojava</groupId>
<artifactId>biojava-core</artifactId>
<version>5.3.0</version>
</dependency>Then, write a simple program to read a DNA sequence:
import org.biojava.nbio.core.sequence.DNASequence;
import org.biojava.nbio.core.sequence.io.FastaReaderHelper;
import java.io.File;
import java.util.LinkedHashMap;
public class BioJavaExample {
public static void main(String[] args) throws Exception {
// Reading a DNA sequence from a FASTA file
LinkedHashMap<String, DNASequence> dnaSequences =
FastaReaderHelper.readFastaDNASequence(new File("path/to/your/fastafile.fasta"));
for (String key : dnaSequences.keySet()) {
System.out.println("Key: " + key);
System.out.println("Sequence: " + dnaSequences.get(key).getSequenceAsString());
}
}
}In this example, we use BioJava to read DNA sequences from a FASTA file, a common format in bioinformatics.
Smile: Machine Learning and Data Analysis
Smile (Statistical Machine Intelligence and Learning Engine) is a library for machine learning, natural language processing, data mining, and other artificial intelligence tasks. It provides a wide range of algorithms and tools for data analysis.
Here’s a simple example of using Smile for clustering:
First, include Smile in your project dependencies. For Maven:
<dependency>
<groupId>com.github.haifengl</groupId>
<artifactId>smile-core</artifactId>
<version>2.6.0</version>
</dependency>Then, you can use Smile to perform K-means clustering:
import smile.clustering.KMeans;
import smile.data.measure.EuclideanDistance;
public class SmileExample {
public static void main(String[] args) {
// Example data points
double[][] data = { {1.0, 2.0}, {1.5, 1.8}, {5.0, 8.0}, {8.0, 8.0}, {1.0, 0.6}, {9.0, 11.0} };
// Perform K-means clustering with 2 clusters
KMeans kmeans = KMeans.fit(data, 2, new EuclideanDistance());
// Output the cluster labels
int[] labels = kmeans.y;
for (int label : labels) {
System.out.println(label);
}
}
}In this code, we use Smile’s KMeans class to cluster a set of two-dimensional data points into two groups.
These domain-specific libraries not only enhance the capabilities of Java in these areas but also make these complex tasks more accessible to developers who may not be experts in these fields.
Java Performance Enhancement
Performance is a crucial aspect of software development, and Java offers tools specifically designed to analyze and improve the efficiency of your code. Two such tools are JMH (Java Microbenchmark Harness) and JProfiler.
JMH: Java Microbenchmark Harness
JMH is a toolkit developed by the OpenJDK team for creating reliable and easy-to-write microbenchmarks in Java. It’s the industry standard for benchmarking code snippets (microbenchmarks) and helps in identifying performance regressions and improvements.
Here’s a basic example of how to use JMH:
First, you need to add JMH to your project. If you’re using Maven, include the following dependency in your pom.xml:
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.33</version>
</dependency>Then, write a simple benchmark:
import org.openjdk.jmh.annotations.Benchmark;
public class MyBenchmark {
@Benchmark
public void testMethod() {
// Your benchmarked code goes here
int a = 1;
int b = 2;
int sum = a + b;
}
}To run this benchmark, you would typically use the JMH plugin for Maven or Gradle, which automates the process of running JMH benchmark:
<build>
<plugins>
<!-- JMH Benchmarking Plugin -->
<plugin>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-maven-plugin</artifactId>
<version>1.33</version>
<executions>
<execution>
<id>benchmark</id>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>mvn clean install
mvn jmh:benchmarkThe first command, mvn clean install, will compile your project and its dependencies. The second command, mvn jmh:benchmark, will specifically run the JMH benchmarks.
The JMH Maven plugin will take care of correctly setting up the benchmarking environment, running your benchmarks, and reporting the results.
Remember, benchmarking code with JMH is intended to measure the performance of small code snippets (microbenchmarks) accurately. The results can give you insights into the efficiency of your code, helping you identify hotspots or areas for improvement.
JProfiler: Profiling Java Applications
JProfiler is an advanced tool for profiling Java applications. It provides detailed insights into how your application uses memory, how threads are managed, and where performance bottlenecks occur. JProfiler’s GUI makes it easy to navigate through complex data to pinpoint the root causes of performance issues.
Since JProfiler is a graphical tool, there’s no code example for it, but here’s a brief guide on how to use it:
- Install JProfiler: Download and install JProfiler from its official website.
- Run Your Application with JProfiler: Start your Java application with JProfiler. You can do this from within JProfiler by specifying your application’s main class and classpath, or you can attach JProfiler to a running Java process.
- Analyze Performance Data: Once your application is running with JProfiler attached, use the JProfiler GUI to analyze the data. You can view different aspects of your application’s performance, such as:
- CPU Usage: See which methods are consuming the most CPU time.
- Memory Usage: Track object creation and garbage collection.
- Thread Profiling: Analyze thread behavior and identify deadlocks.
- Identify Bottlenecks: Use JProfiler’s tools to drill down into performance data and identify bottlenecks and inefficiencies in your code.
Together, these tools form a comprehensive toolkit for enhancing the performance of Java applications.
Improving Java Development Workflow
In Java development, enhancing productivity and streamlining the workflow are crucial. Two tools that significantly contribute to this are JRebel and MapStruct. While JRebel focuses on speeding up the development cycle by instant code reloading, MapStruct offers a clean way to map between different Java object types.
JRebel: Instant Code Reloading
JRebel is a productivity tool that allows Java developers to instantly reload code changes in their development server without restarting the application. This means faster turnaround times for seeing the effects of code modifications, which is particularly valuable in large applications where restart times can be significant.
Here’s a general idea of how JRebel works:
- Install JRebel: First, install the JRebel plugin in your IDE (Integrated Development Environment). This plugin is available for most popular Java IDEs like IntelliJ IDEA, Eclipse, and NetBeans.
- Configure Your Project: After installation, configure JRebel for your project. This typically involves adding a JRebel configuration file to your project.
- Run with JRebel: Start your application server with JRebel. In your IDE, you can usually do this by clicking a JRebel-specific button or selecting a JRebel run configuration.
- Make Changes and See Them Instantly: Now, when you make changes to your code, JRebel reloads those changes in the running application instantly. There’s no need to restart the server or redeploy the application.
MapStruct: Simplifying Object-to-Object Mapping
MapStruct is a code generator that simplifies the implementation of mappings between Java bean types. It works at compile time to generate the actual mapping code, ensuring fast execution and reducing the chance of runtime errors.
Suppose you have two classes, Car and CarDto, and you need to map between these two. Here’s how you can use MapStruct to do this:
First, add MapStruct to your Maven project:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>Then, create a mapper interface:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}In this example, CarMapper is an interface that MapStruct uses to generate the implementation. The @Mapping annotation is used to specify how the fields in the Car class map to the fields in the CarDto class.
By incorporating JRebel and MapStruct into the development workflow, Java developers can significantly enhance their productivity.
Conclusion
Each library discussed in this article extend the depth and breadth of Java’s capabilities, offering powerful, efficient, and often elegant solutions for a wide range of programming challenges. These tools, though less known, deserve recognition for their role in enhancing and refining the art of Java programming.
Until next time, keep coding, innovating, and never stop learning. Cheers, fellow developers!
If you enjoyed reading my article, please consider buying me a coffee 💗 and stay tuned to more articles about Java, tech, and AI 👩🏻💻
