I'm new to swagger and looking to get it set up on my latest REST API project but am failing miserably. Everything seems to be configured correctly but I am unable to navigate to swagger.json without getting a 404. I can't see what is missing or wrong
pom.xml
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jersey-jaxrs</artifactId>
<version>1.5.0</version>
<exclusions>
<exclusion>
<artifactId>jersey-server</artifactId>
<groupId>com.sun.jersey</groupId>
</exclusion>
<exclusion>
<artifactId>jersey-multipart</artifactId>
<groupId>com.sun.jersey.contribs</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-multipart</artifactId>
<version>1.11.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.11.1</version>
</dependency>
Exclusions as the project uses version 1.11.1 and causes conflicts if 1.13 is imported from swagger
web.xml
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
io.swagger.jaxrs.json,
io.swagger.jaxrs.listing,
uk.package.test.myproject
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Jersey2Config</servlet-name>
<servlet-class>io.swagger.jaxrs.config.DefaultJaxrsConfig</servlet-class>
<init-param>
<param-name>api.version</param-name>
<param-value>1.0.0</param-value>
</init-param>
<init-param>
<param-name>swagger.api.basepath</param-name>
<param-value>http://localhost:8082/myproject/api</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
Example annotations...
#ManagedBean
#Path("secure/cache")
#Restrict("ManageCache")
#Api(value = "secure/cache", description = "Rest api for do operations on admin", produces = MediaType.APPLICATION_JSON)
public class AdminApi {....
#GET
#Path("{name}")
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Get specific admin", httpMethod = "GET", notes = "Fetch the admin user details", response = Response.class)
#ApiResponses(value = { #ApiResponse(code = 200, message = "Given admin user found"),
#ApiResponse(code = 404, message = "Given admin user not found"),
#ApiResponse(code = 500, message = "Internal server error due to encoding the data"),
#ApiResponse(code = 400, message = "Bad request due to decoding the data"),
#ApiResponse(code = 412, message = "Pre condition failed due to required data not found") })
public List<String> getCacheKeys(.....
When the project is run the following is logged...
10:46:48,724 INFO [com.sun.jersey.api.core.PackagesResourceConfig] (MSC service thread 1-5) Scanning for root resource and provider classes in the packages:
io.swagger.jaxrs.json
io.swagger.jaxrs.listing
uk.package.test.myproject
10:46:48,771 INFO [com.sun.jersey.api.core.ScanningResourceConfig] (MSC service thread 1-5) Root resource classes found:
class uk.package.test.myproject.api.NotificationApi
class uk.package.test.myproject.api.UserApi
class uk.package.test.myproject.api.FlagsApi
class uk.package.test.myproject.api.UnitsByFunctionApi
class uk.package.test.myproject.api.CvDataApi
class uk.package.test.myproject.api.ImageApi
class uk.package.test.myproject.api.GazetteerApi
class uk.package.test.myproject.api.SearchApi
class uk.package.test.myproject.api.ViewApi
class uk.package.test.myproject.api.AdminApi
10:46:48,776 INFO [com.sun.jersey.api.core.ScanningResourceConfig] (MSC service thread 1-5) No provider classes found.
10:46:48,834 INFO [com.sun.jersey.server.impl.cdi.CDIComponentProviderFactoryInitializer] (MSC service thread 1-5) CDI support is enabled
10:46:48,842 INFO [com.sun.jersey.server.impl.application.WebApplicationImpl] (MSC service thread 1-5) Initiating Jersey application, version 'Jersey: 1.11.1 03/31/2012 06:49 PM'
Thank you in advance
Cheers
Martin
Just to let you know I have resolved this by using a custom Application rather than using package scanning :)
Thanks for your help
Martin
Your servlet mapping says that you should not be looking at the path you specified:
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
That typically means to look under the /api path. That means then, unless you're using a webapp called myproject, you should be looking under /api/swagger.json
Related
I am running a Jersey 2.30.1 API and I want to upgrade the Swagger documentation to OpenAPI v3.
My new swagger dependencies in the pom.xml look like this:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>swagger-ui</artifactId>
<version>3.17.0</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-servlet-initializer</artifactId>
<version>2.0.2</version>
</dependency>
Previously (swagger 1.5 with swagger-ui 2.2.10-1), I had Swagger configured in my web.xml:
<servlet>
<servlet-name>Jersey2Config</servlet-name>
<servlet-class>io.swagger.jersey.config.JerseyJaxrsConfig</servlet-class>
<init-param>
<param-name>api.version</param-name>
<param-value>2.0</param-value>
</init-param>
<init-param>
<param-name>swagger.api.basepath</param-name>
<param-value>/api</param-value>
</init-param>
<load-on-startup>4</load-on-startup>
</servlet>
I would like to do the same now, but I can't find a documentation, if and how that would be possible.
How can I define the swagger base path and API version in my web.xml with OpenAPI v3?
After playing around with this, my solution is basically like so.
pom.xml:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>swagger-ui</artifactId>
<version>3.52.1</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2</artifactId>
<version>2.1.10</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-servlet-initializer</artifactId>
<version>2.1.10</version>
</dependency>
web.xml:
The Jersey2Config servlet is completely removed from web.xml.
The servlet configuration needs to be changed for the parameter io.swagger.v3.jaxrs2.integration.resources
<servlet>
<servlet-name>my servlet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>my.package.to.scan, io.swagger.v3.jaxrs2.integration.resources</param-value>
</init-param>
</servlet>
Additional to web.xml a new file openapi.yaml has to be created under src/main/webapp/WEB-INF. This is responsible for creating the openapi.yaml/ openapi.json file which will be consumed by the swagger ui.
openapi.yaml:
prettyPrint: true
cacheTTL: 0
readAllResources: false /** Only show methods in swagger-ui that are annotated */
openAPI:
info:
version: '1.0'
title: 'my application'
servers:
- url: '/my-application'
swagger ui:
...
window.onload = function() {
const window.ui = SwaggerUIBundle({
url: 'https://my-domain.com/my-application/openapi.json',
dom_id: '#swagger-ui-container',
docExpansion: 'none',
defaultModelsExpandDepth: -1,
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
apisSorter: "alpha",
tagsSorter: "alpha",
operationsSorter: "alpha",
validatorUrl: null
});
}
...
I have found a few questions like this on SO already but none of them seemed to address my particular problem, and I have been unable to find a solution on my own.
Here is the error I'm getting:
Caused by: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=multipart/form-data; boundary=----WebKitFormBoundaryHWk1XUaeu7pEiDth, type=class org.glassfish.jersey.media.multipart.FormDataMultiPart, genericType=class org.glassfish.jersey.media.multipart.FormDataMultiPart.
I am sending this through a jQuery AJAX request that looks like this:
$('#upload-image-form').on('submit', function(e) {
e.preventDefault();
var data = new FormData(this);
$.ajax({
url: url,
method: 'POST',
contentType: false,
processData: false,
data: data,
}).done(function(data) {
console.log(data);
}).fail(function(res, status) {
onError(res, status, 'Image upload failed');
});
});
And this is my Java endpoint:
#POST
#Path("/{userId}")
#Consumes("multipart/form-data")
public Response createGraphic(
#PathParam("userId") int userId,
FormDataMultiPart multiPartFormData) { ... }
I have seen a few people have luck with changing the parameter of the endpoint method to use #FormDataParam instead of FormDataMultiPart (as seen here), but I cannot edit the Java class, so I must use it how it is above.
My pom.xml has the following dependencies:
<dependency>
<groupId>org.jvnet</groupId>
<artifactId>mimepull</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.12</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.12</version>
</dependency>
web.xml
<servlet>
<servlet-name>Jersey</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>my.package</param-value>
</init-param>
<load-on-startup>5</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
The only other thing I was able to dig up was to register the MultiPartFeature using a ResourceConfig; however, the project I'm working with does not have any Application classes or any class that extends ResourceConfig (it's a WAR that's deployed to Tomcat, so no main class).
Is there any other configuration that needs to be done? I'm stumped as to why this is not working.
The MultiPartFeature has the required reader and writer. But you still need to register the feature. As you've mentioned, you will often see it's registration in an Application/ResourceConfig subclass. But in a web.xml, you can simply add it to the list of classes to add as provider. You can do that by adding an <init-param> to the servlet configuration, i.e.
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
org.glassfish.jersey.media.multipart.MultiPartFeature,
some.other.Provider
</param-value>
</init-param>
As you can see in the example, if you need to register any other providers/features, you comma-delimit the class names.
I am trying to introduce Jersey web service into a web application that has other web services (such as Apache CXF in it) in it. So I added Jersey servlet to my web.xml..
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>
com.sun.jersey.spi.spring.container.servlet.SpringServlet
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.freedomoss.crowdcontrol.api</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-servlet</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
During intiliazation this servlet gives the following error about conflicting URL templates...
INFO: Registering Spring bean, restApiService, of type com.mycompany.ws.RestApiService as a root resource class
Mar 12, 2015 11:07:42 PM com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Conflicting URI templates. The URI template / for root resource class com.mycompany.ws.RestApiService and the URI template / transform to the same regular expression (/.*)?
When I look in the code of this RestApiService, I see..
#Path("/")
#Service
public class RestApiService extends AbstractRestService {
Since this other web service is being used elsewhere in the application I cannot change the value of 'Path' although doing so does solve my problem
What else can I do? Can I somehow tell Jersey Servlet not to register this other web service "as a root resource class"?
My goal is to make Jersey work along with this other web service and not change the latter in any way.
I have a simple RESTful Java web service...
#Service
#Path("/getAccountBalance")
public class GetAccountBalanceService {
#Autowired
private ILicenseService licenseService;
#GET
#Path("/{param}")
public Response provideService(#PathParam("param") String licenseUUID) {
License license = this.licenseService.getByUUID(licenseUUID);
String output = "Balance on the account : " + license.getBalanceValue();
return Response.status(200).entity(output).build();
}
At first I was getting an error where the bean licenseService was null even though I am autowiring it. So based on advice from another post I swtiched to use the Spring servlet for jersey, thus in my web-xml I changed from com.sun.jersey.spi.container.servlet.ServletContainer to...
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>
com.sun.jersey.spi.spring.container.servlet.SpringServlet
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.freedomoss.crowdcontrol.api</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
But now when I start Tomcat I am getting the error..
Dec 19, 2014 12:13:13 AM org.apache.catalina.startup.ContextConfig applicationWebConfig
SEVERE: Parse error in application web.xml file at jndi:/localhost/mturk-web/WEB-INF/web.xml
java.lang.IllegalArgumentException: Servlet mapping specifies an unknown servlet name jersey-serlvet
Why is this happening? As you can see there is a servlet defined with that name in web-xml. If there was something wrong with loading that servlet why don't the logs tell that story?
Thanks.
I am able to access REST services from the browser url: http://localhost:8080/assignment/services/services/test/test1
From My servlet, I use to call service method as shown below. Now I need to call through REST services but getting below error.
URL url = new URL("http://localhost:8080/assignment/services/services/"+userName+"/"+password);
System.out.println("URL-->"+url);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/xml");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
while(bufferedReader.readLine() != null){
result = bufferedReader.readLine();
}
// result = userService.login(userName, password);
System.out.println(result);
here is the error:
INFO: Reloading Context with name [/assignment] is completed
URL-->http://localhost:8080/assignment/services/test/test1
Jul 30, 2013 12:52:02 PM org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor processRequest
WARNING: No root resource matching request path /assignment/services/test/test1 has been found, Relative Path: /test/test1. Please enable FINE/TRACE log level for more details.
Jul 30, 2013 12:52:02 PM org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper toResponse
WARNING: javax.ws.rs.NotFoundException
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:172)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:100)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:271)
at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:239)
at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:223)
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:203)
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:137)
at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:158)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java
...
Jul 30, 2013 12:52:02 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [LoginServlet] in context with path [/assignment] threw exception
java.io.FileNotFoundException: http://localhost:8080/assignment/services/services/test/test1
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1623)
at com.viasat.test.login.servlet.LoginServlet.process(LoginServlet.java:77)
at com.viasat.test.login.servlet.LoginServlet.doPost(LoginServlet.java:52)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
Service class:
#Service
#Path("/services/")
public class UserServiceImpl implements UserService {
#GET
#Path("{userName}/{password}")
#Produces(MediaType.TEXT_XML)
public String login(#PathParam("userName")String username, #PathParam("password")String password)
throws JAXBException, PropertyException, FileNotFoundException {
web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/beans.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>cxf</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<display-name>LoginServlet</display-name>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.abc.test.login.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/LoginServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>cxf</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
</web-app>
Update:
pom.xml:
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
Now Javax.ws.rs.NotFoundException gone, but, File not found exception still comes.
Solution-1
One of the solution is that version issue with CXF and Spring. I have made cxf version to 2.5.2 which avoids javax.ws.rs.NotFoundException.
Now I have updated to
<spring.version>3.2.2.RELEASE</spring.version>
<cxf.version>2.5.2</cxf.version>
I was using 2.7.5 cxf version.
Solution -2
In my service class I have made
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) instead of
#Produces(MediaType.TEXT_XML)
The URL you are using to fire the GET request has two services levels:
http://localhost:8080/assignment/services/services/"
but in your REST service class, there is only one 'services' level in Path. You may need to change the Path Param to like this:
#Service
#Path("/services/")
public class UserServiceImpl implements UserService {
#GET
#Path("services/{userName}/{password}")
#Produces(MediaType.TEXT_XML)
public String login(#PathParam("userName")String username, #PathParam("password")String password)
throws JAXBException, PropertyException, FileNotFoundException {
I think "services" is by default the path where CXF exposes the auto-generated WADLs and therefore cannot be used in a resource path.
Try either changing "services" to another word in your web.xml and Service class (#Path) or change the CXF servlet config like so (see the service-list-path param):
<servlet>
<servlet-name>cxf</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<init-param>
<param-name>service-list-path</param-name>
<param-value>web-services</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Reference: http://cxf.apache.org/docs/jaxrs-services-description.html (section Service listings and WADL queries almost at the end).
Note that you can override the location at which listings are provided
(in case you would like '/services' be available to your resources)
using 'service-list-path' CXFServlet parameter
one common reason for this error is that cxf cannot find a restful implementation for the restful service to match the request to the resource method later. the spec is ambiguious in this area but your jaxrs annotations in the interface must match implementation in cxf.
--eliani
As described in CXF project throws java.lang.NoClassDefFoundError: javax/ws/rs/NotFoundException the
Error can be solved by adding
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0-m10</version>
</dependency>