Hello Community,
I have been stuck at this issue for a long time now and need help in identifying what am I doing wrong.
I have Jetty 11 and Jersey 3.0.2 and Swagger-core 2.1.9. I am wondering if any of the following are wrong in my project:
- Jetty & Jersey integration
- Dependencies
w.r.to Jetty and Jersey
- Annotations of API
My control flow is as follows:
- Created jetty server that has 128 queue size
- Created a jetty servlet holder
- Added Jersey servlet so intercept / scan all my API paths
- I use Jakarta namespace for this project (you can find in the bottommost gradle for the dependencies )
But when I run the server with the above setting, I get:
Caused by: org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource model has failed during application initialization.
However, all my API calls are accessible without jersey or swagger servlet added and I confirmed this by exercising them using python script and also using postman. Now that I want to generate a swagger.json or openapi.yaml for all my apis, I try to scan
using jersey model as follows:
// Setup API resources
ServletHolder jersey = servletContextHandler.addServlet(ServletContainer.class, "/api/*");
jersey.setInitOrder(1);
jersey.setInitParameter("jersey.config.server.provider.packages", "com.cloudian.hfs.handlers;io.swagger.v3.jaxrs2.integration.resources");
Where com.cloudian.hfs.handlers contain all resource classes and apis. What am I doing wrong? For detailed code:
Jersy and Jetty integration:
import com.cloudian.hfs.handlers.*;
import io.swagger.v3.jaxrs2.integration.OpenApiServlet;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.servlet.ServletContainer;
import java.util.logging.Level;
import java.util.logging.Logger;
public class StartHFS {
public static void main(String[] args) throws Exception {
System.out.println("StartHFS");
// Create and configure a ThreadPool.
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setName("server");
// Create a Server instance.
Server server = new Server(threadPool);
// HTTP configuration and connection factory.
HttpConfiguration httpConfig = new HttpConfiguration();
HttpConnectionFactory http11 = new HttpConnectionFactory(httpConfig);
// Create a ServerConnector to accept connections from clients.
ServerConnector connector = new ServerConnector(server, 1, 1, http11);
connector.setPort(8080);
connector.setHost("0.0.0.0");
connector.setAcceptQueueSize(128);
server.addConnector(connector);
addHandlers(server);
// Start the Server so it starts accepting connections from clients.
try {
server.start();
server.join();
} catch (Exception ex) {
Logger.getLogger(StartHFS.class.getName()).log(Level.SEVERE, "Fail to start HFS Server", ex);
} finally {
server.destroy();
}
System.out.println("StartHFS DONE");
}
static void addHandlers(final Server server) throws Exception {
ContextHandlerCollection contexts = new ContextHandlerCollection();
server.setHandler(contexts);
ContextHandler featureStoreHandler = new ContextHandler("/featurestore");
featureStoreHandler.setHandler(new FeatureStoreHandler());
contexts.addHandler(featureStoreHandler);
// Setup Jetty Servlet
ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContextHandler.setContextPath("/");
contexts.addHandler(servletContextHandler);
// Setup API resources
ServletHolder jersey = servletContextHandler.addServlet(ServletContainer.class, "/api/*");
jersey.setInitOrder(1);
jersey.setInitParameter("jersey.config.server.provider.packages", "com.cloudian.hfs.handlers;io.swagger.v3.jaxrs2.integration.resources");
// Expose API definition independently into yaml/json
ServletHolder openApi = servletContextHandler.addServlet(OpenApiServlet.class, "/openapi/*");
openApi.setInitOrder(2);
openApi.setInitParameter("openApi.configuration.resourcePackages", "com.cloudian.handlers;io.swagger.sample.resource");
}
}
Gradle Dependencies:
plugins {
id 'java'
id "io.swagger.core.v3.swagger-gradle-plugin" version "2.1.9"
}
group 'com.cloudian'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
// Basic Gradle dependencies
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
// Logging dependencies
runtimeOnly 'org.slf4j:slf4j-api:1.7.7'
runtimeOnly 'org.slf4j:slf4j-log4j12:1.7.7'
// Jetty dependencies
implementation 'org.eclipse.jetty:jetty-server:11.0.0'
implementation 'org.eclipse.jetty:jetty-servlet:11.0.0'
implementation 'org.eclipse.jetty:jetty-util:11.0.0'
// Jersey dependencies
implementation 'org.glassfish.jersey.containers:jersey-container-jetty-http:3.0.2'
implementation 'org.glassfish.jersey.containers:jersey-container-servlet-core:3.0.2'
// implementation 'org.glassfish.jersey.core:jersey-server:3.0.2'
implementation 'org.glassfish.jersey.media:jersey-media-json-binding:3.0.2'
implementation 'org.glassfish.jersey.core:jersey-common:3.0.2'
// Jetty and Jersey InjectionManagerFactory dependency [Required]
implementation 'org.glassfish.jersey.inject:jersey-hk2:3.0.2'
// Enable JAXBContext and WADL Jersey [Required]
implementation 'org.glassfish.jaxb:jaxb-runtime:3.0.1'
// Redis & Json dependencies
implementation 'org.springframework.data:spring-data-redis:2.4.3'
implementation 'redis.clients:jedis:3.3.0'
implementation group: 'org.json', name: 'json', version: '20201115'
// Swagger, Jakarta deps
implementation 'org.apache.commons:commons-lang3:3.7'
implementation 'io.swagger.core.v3:swagger-jaxrs2-jakarta:2.1.9'
implementation 'jakarta.ws.rs:jakarta.ws.rs-api:3.0.0'
implementation 'jakarta.servlet:jakarta.servlet-api:5.0.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
resolve {
outputFileName = 'MyRestAPI'
outputFormat = 'YAML'
prettyPrint = 'TRUE'
classpath = sourceSets.main.runtimeClasspath
buildClasspath = classpath
resourcePackages = ['io.test']
outputDir = file('test')
}
test {
useJUnitPlatform()
}
jar {
manifest {
attributes "Main-Class": "com.cloudian.hfs.StartHFS"
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
Annotation of my APIs:
@Path("/featurestore/")
@Produces({"application/json", "application/xml"})
public class FeatureStoreHandler extends AbstractHandler {
// Some final CONSTANTS for api implementation
@Override
public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {}
@POST
@Path("/create")
@Consumes("application/json")
public void createFeatureGroup(@RequestBody Request jettyRequest, @RequestBody HttpServletRequest request, HttpServletResponse response) throws IOException, JSONException, JedisException {}
@DELETE
@Path("/delete")
@Consumes("application/json")
public void deleteFeatureGroup(@RequestBody Request jettyRequest, @RequestBody HttpServletRequest request, HttpServletResponse response) throws IOException, JedisException, JSONException {}
@GET
@Path("/describe")
public void describeFeatureGroup(@RequestBody Request jettyRequest, @RequestBody HttpServletRequest request, HttpServletResponse response) throws IOException, JedisException, JSONException {}
}