Using Java
Learn how to use Java with Helidon in the JAQ Stack ecosystem.
Overview
Java with Helidon is the backbone of the JAQ Stack, providing lightweight, cloud-native backend services with enterprise-grade functionality and performance.
Getting Started
Prerequisites
- Java 11 or higher
- Maven 3.6+
- IDE (IntelliJ IDEA, Eclipse, or VS Code)
Installation
# Clone the repository
git clone https://github.com/jaqstack/jaqstack-java.git
# Navigate to the project
cd jaqstack-java
# Build the project
mvn clean install
Core Features
- Helidon SE: Lightweight microservices framework
- Cloud Native: Built for containers and Kubernetes
- Security: Built-in authentication and authorization
- Database Connectivity: Seamless integration with NoSQL databases
- RESTful APIs: Clean and consistent API design with JAX-RS
- Observability: Built-in metrics, health checks, and tracing
- Testing: Comprehensive test suite included
Configuration
Application Configuration
# application.yaml
server:
port: 8080
host: 0.0.0.0
app:
name: jaqstack-backend
version: 1.0.0
database:
mongodb:
host: localhost
port: 27017
database: jaqstack
username: admin
password: password
security:
jwt:
secret: your-jwt-secret
expiration: 3600
logging:
level:
io.helidon: INFO
com.jaqstack: DEBUG
Maven Dependencies
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>io.helidon.microprofile.bundles</groupId>
<artifactId>helidon-microprofile</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>io.helidon.integrations.mongo</groupId>
<artifactId>helidon-integrations-mongo</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>io.helidon.security</groupId>
<artifactId>helidon-security</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>io.helidon.metrics</groupId>
<artifactId>helidon-metrics</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>
Best Practices
- Code Organization: Follow the standard Helidon project structure
- Error Handling: Use global exception mappers
- Validation: Implement proper input validation with Bean Validation
- Logging: Use structured logging with appropriate levels
- Documentation: Keep API documentation up to date with OpenAPI
- Configuration: Use external configuration for environment-specific settings
- Health Checks: Implement comprehensive health checks
Examples
Creating a REST Resource
@Path("/api/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {
@Inject
private UserService userService;
@GET
public Response getAllUsers() {
List<User> users = userService.findAll();
return Response.ok(users).build();
}
@POST
public Response createUser(@Valid User user) {
User createdUser = userService.save(user);
return Response.status(Response.Status.CREATED)
.entity(createdUser)
.build();
}
@GET
@Path("/{id}")
public Response getUserById(@PathParam("id") String id) {
return userService.findById(id)
.map(user -> Response.ok(user).build())
.orElse(Response.status(Response.Status.NOT_FOUND).build());
}
}
Service Layer
@ApplicationScoped
public class UserService {
@Inject
private UserRepository userRepository;
public List<User> findAll() {
return userRepository.findAll();
}
public User save(User user) {
return userRepository.save(user);
}
public Optional<User> findById(String id) {
return userRepository.findById(id);
}
public void deleteById(String id) {
userRepository.deleteById(id);
}
}
Repository Layer
@ApplicationScoped
public class UserRepository {
@Inject
private MongoClient mongoClient;
private MongoCollection<User> getCollection() {
return mongoClient.getDatabase("jaqstack")
.getCollection("users", User.class);
}
public List<User> findAll() {
return getCollection().find().into(new ArrayList<>());
}
public User save(User user) {
if (user.getId() == null) {
user.setId(UUID.randomUUID().toString());
}
getCollection().replaceOne(
eq("_id", user.getId()),
user,
new ReplaceOptions().upsert(true)
);
return user;
}
public Optional<User> findById(String id) {
User user = getCollection().find(eq("_id", id)).first();
return Optional.ofNullable(user);
}
public void deleteById(String id) {
getCollection().deleteOne(eq("_id", id));
}
}
Application Main Class
public class Main {
public static void main(String[] args) {
Server server = Server.builder()
.port(8080)
.routing(r -> r
.register("/api", new UserResource())
.register("/health", HealthSupport.create())
.register("/metrics", MetricsSupport.create())
)
.build();
server.start();
}
}
Advanced Features
Security Configuration
@ApplicationScoped
public class SecurityConfig {
@Produces
public Security security() {
return Security.builder()
.addProvider(basicAuthProvider())
.addProvider(jwtProvider())
.build();
}
private AuthenticationProvider basicAuthProvider() {
return HttpBasicAuthProvider.builder()
.userStore(userStore())
.build();
}
private AuthenticationProvider jwtProvider() {
return JwtProvider.builder()
.verifyKey(publicKey())
.build();
}
}
Health Checks
@ApplicationScoped
public class DatabaseHealthCheck implements HealthCheck {
@Inject
private MongoClient mongoClient;
@Override
public HealthCheckResponse call() {
try {
mongoClient.getDatabase("jaqstack").runCommand(new Document("ping", 1));
return HealthCheckResponse.named("database")
.status(HealthCheckResponse.Status.UP)
.withData("database", "jaqstack")
.build();
} catch (Exception e) {
return HealthCheckResponse.named("database")
.status(HealthCheckResponse.Status.DOWN)
.withData("error", e.getMessage())
.build();
}
}
}
Metrics
@ApplicationScoped
public class UserMetrics {
private final Counter userCreationCounter;
private final Timer userQueryTimer;
public UserMetrics(MeterRegistry meterRegistry) {
this.userCreationCounter = Counter.builder("user.creation.total")
.description("Total number of users created")
.register(meterRegistry);
this.userQueryTimer = Timer.builder("user.query.duration")
.description("User query duration")
.register(meterRegistry);
}
public void incrementUserCreation() {
userCreationCounter.increment();
}
public void recordQueryDuration(Duration duration) {
userQueryTimer.record(duration);
}
}
Troubleshooting
Common Issues
- Port Already in Use: Change the server port in application.yaml
- Database Connection: Ensure MongoDB is running and accessible
- Memory Issues: Increase JVM heap size for large applications
- CDI Issues: Ensure proper CDI annotations are used
Getting Help
- Check the GitHub Issues
- Join our Discord Community
- Read the Helidon Documentation
- Read the FAQ