this is going to be a longer question due to the required information to make it clear. I am currently writing a Microservice Application in Java with each Application running on an OpenLiberty Server and containerized using docker, so far so good.
This is the first time for me writing such an application and I am having to learn a lot of things at once which is fine, my Problem is the proper understanding of how to actually get the applications to run correctly on the OpenLiberty Server.
To be more precise it seems to me like either the ports I am exposing are not exposing correctly or my Source Code is causing the server to act weirdly.
I will give you an example of two classes within one of the services which, the source code is the following:
package com.coffeeshop.microservice;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.agent.model.NewService;
import com.fasterxml.jackson.core.JsonProcessingException;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.sql.SQLException;
import java.util.UUID;
import java.util.logging.Logger;
import jakarta.annotation.PostConstruct;
/**
* A RESTful web service that exposes endpoints related to order creation and management.
*/
@Path("/orders")
public class OrderFacade {
@Inject
private OrderService orderService;
/**
* Creates a new order using the specified order details.
* @param order the order details.
* @return a response indicating whether the order was created successfully.
* @throws JsonProcessingException if there was an error serializing the order to JSON.
* @throws SQLException if there was an error creating the order in the database.
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createOrder(Order order) throws JsonProcessingException, SQLException {
// Call the createOrder method of the OrderService class
Response createdOrder = orderService.createOrder(order);
// Check if the order was successfully created
if (createdOrder.getStatus() == Response.Status.CREATED.getStatusCode()) {
// Forward the order to the product service
orderService.forwardOrder("http://localhost:9081/product-service/process-order", order);
}
return createdOrder;
}
}
and class number two:
package com.coffeeshop.microservice;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import com.fasterxml.jackson.core.JsonProcessingException;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.core.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @author Sean Bäker
* @version 1.0
* @since April.2023
* This class represents the Order Service for the CoffeeShop microservice architecture.
* It handles the creation of new orders, as well as forwarding the order to the Product Service
* for further processing.
* The Order Service is implemented as a RESTful web service using JAX-RS and the Jakarta RESTful Web Services API.
*/
@Path("/orders")
public class OrderService{
String SQL = "INSERT into orders (item) VALUES(?)";
public String connectionPoolUrl;
/**
* Constructor for the OrderService class.
* @param connectionPoolUrl The URL of the connection pool service.
*/
public OrderService(String connectionPoolUrl){
this.connectionPoolUrl = connectionPoolUrl;
}
/**
* This method creates a new order and forwards it to the Product Service for further processing.
* @param order The Order object representing the new order to be created.
* @return A JAX-RS Response object indicating the status of the order creation process.
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("/create-order")
public Response createOrder(Order order){
Connection conn;
try {
conn = getConnectionFromPool("http://localhost:9084/connection-pool/get-connection");
PreparedStatement pstmt = conn.prepareStatement(SQL);
pstmt.setString(1, order.getItem());
pstmt.executeUpdate();
forwardOrder("http://localhost:9081/product-service/process-order", order);
return Response.status(Response.Status.CREATED).entity(order).build();
} catch (SQLException sqlException) {
//Fluentd LOGGER is to be added here
sqlException.printStackTrace();
}
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
/**
* This method forwards an order to the specified URL using an HTTP POST request.
* @param url The URL to which the order is to be forwarded.
* @param order The Order object representing the order to be forwarded.
*/
public void forwardOrder(String url, Order order){
String json = serializeJson(order);
try {
OkHttpClient ok = new OkHttpClient();
RequestBody requestBody = RequestBody.create(MediaType.APPLICATION_JSON, okhttp3.MediaType.parse(json));
okhttp3.Request request = new okhttp3.Request.Builder()
.url(url)
.post(requestBody)
.build();
try (okhttp3.Response response = ok.newCall(request).execute()){
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
}
} catch(IOException e) {
e.printStackTrace();
}
}
/**
* This method serializes an Order object into JSON format.
* @param order The Order object to be serialized.
* @return A String representation of the Order object in JSON format.
*/
public String serializeJson(Order order){
try {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(order);
} catch(JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
/**
* Gets a connection from the connection pool service using a REST API call.
* @param url the URL of the connection pool service
* @return a Connection object from the connection pool
* @throws SQLException if a database access error occurs
*/
private Connection getConnectionFromPool(String url) throws SQLException{
jakarta.ws.rs.core.Response response = ClientBuilder.newClient()
.target(url)
.path("get-connection")
.request(MediaType.APPLICATION_JSON)
.get();
return response.readEntity(Connection.class);
}
}
Now what these classes are responsible is a.) the orderfacade is supposed to be the entrypoint for the user to reach http://localhost:9080/orders
at which he should be able to place an order which is a single item within an JSON in this form: '{"item":"Latte"}'
.
Now when I package my application in dockerfiles which generally look like this:
FROM icr.io/appcafe/open-liberty:kernel-slim-java8-openj9-ubi
COPY --chown=1001:0 src/main/liberty/config/server.xml /config/
COPY --chown=1001:0 target/orderservice-microservice.war /config/dropins/
I can reach http://localhost:9080
but not the /orders
endpoint, here I mostly get ERR_EMPTY_RESPONSE
. For my understanding I have configured the server correctly, the server.xml
for the OpenLiberty server for the shown service above is:
<?xml version="1.0" encoding="UTF-8"?>
<server description="${project.name}">
<httpEndpoint id="defaultHttpEndpoint"
host= "*"
httpPort="9080"
httpsPort="9443"/>
<webApplication location="${project.name}.war" contextRoot="${app.context.root}">
<classloader apiTypeVisibility="+third-party" />
</webApplication>
<mpMetrics authentication="false"/>
<!-- This is the keystore that will be used by SSL and by JWT. -->
<keyStore id="defaultKeyStore" location="public.jks" type="JKS" password="atbash" />
<!-- The MP JWT configuration that injects the caller's JWT into a ResourceScoped bean for inspection. -->
<mpJwt id="jwtUserConsumer" keyName="theKeyId" audiences="targetService" issuer="${jwt.issuer}"/>
</server>
I have already tried exposing different ports, opening the ports on the firewall settings in my windows machine. But nothing seems to be working.