I recently followed this blog to integrate swagger in my embedded jetty project but after running, I'm not able to access the swagger.json file on any path combination. Accessing the servlets for the resources work with no error but I get the following errors when I try to get the swagger.json file
http://host:7000/swagger-core ===> HTTP ERROR 405
http://host:7000/swagger-core/swagger.json ===> HTTP ERROR 404
http://host:7000/user/swagger.json ===> HTTP ProfileServlet response, not swagger.json
http://host:7000/user ===> HTTP ProfileServlet response, not swagger.json
http://host:7000/swagger.json ===> HTTP ERROR 404
http://host:7000/api/swagger.json ===> HTTP ERROR 404
http://host:7000/ ===> Static swagger sample page (Pet store), not swagger.json
Main.java
public static void main(String[] args) throws Exception {
Server server = initializeApi(properties);
server.start();
logger.info("Api resource service started");
server.join();
}
private static Server initializeApi(Properties properties) {
logger.info("Initializing user profile server...");
new UserDao();
Server server = new Server(Integer.parseInt(properties.getProperty(Config.JETTY_SERVICE_PORT)));
ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContextHandler.setContextPath("/");
server.setHandler(servletContextHandler);
//Setup APIs
ServletHolder apiservlet = servletContextHandler.addServlet(ProfileServlet.class, "/user/*");
apiservlet.setInitOrder(1);
apiservlet.setInitParameter("com.sun.jersey.config.property.packages", "com.api.resources;io.swagger.jaxrs.json;io.swagger.jaxrs.listing");
logger.info("User profile server initialized.");
// Setup Swagger servlet
ServletHolder swaggerServlet = servletContextHandler.addServlet(DefaultJaxrsConfig.class, "/swagger-core");
swaggerServlet.setInitOrder(2);
swaggerServlet.setInitParameter("api.version", "1.0.0");
// Setup Swagger-UI static resources
String resourceBasePath = Main.class.getResource("/webapp").toExternalForm();
servletContextHandler.setWelcomeFiles(new String[] {"index.html"});
servletContextHandler.setResourceBase(resourceBasePath);
servletContextHandler.addServlet(new ServletHolder(new DefaultServlet()), "/*");
return server;
}
}
ProfileServlet.java
#SwaggerDefinition(
info = #Info(
title = "User Profile Servlet",
version = "1.0.0",
description = "Servlet that handles basic CRUD operations to the user profile data source",
contact = #Contact(name = "XYZ", email = "XYZ", url = "XYZ"),
termsOfService = "XYZ",
license = #License(name = "XYZ", url = "XYZ")
),
basePath = "/",
consumes = {"application/json"},
produces = {"application/json"},
schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS},
tags = {#Tag(name = "users", description = "CRUD operations on user datatype")}
)
#Api(value = "/user", description = "performs CRUD operations on a user profile")
public class ProfileServlet extends HttpServlet {
Logger logger = Logger.getLogger(ProfileServlet.class.getSimpleName());
public ProfileServlet(){
}
#ApiOperation(httpMethod = "GET", value = "Returns a list of the user profile datatype", notes = "", response = UserDatatype.class, nickname = "getUser", tags = ("User"))
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Succssful retrieval of user profiles", response = UserDatatype.class),
#ApiResponse(code = 500, message = "Internal server error")
})
#ApiImplicitParams({
#ApiImplicitParam(name = "id", value = "profile id", required = false, dataType = "String", paramType = "query"),
#ApiImplicitParam(name = "firstname", value = "First name of user", required = false, dataType = "String", paramType = "query"),
#ApiImplicitParam(name = "lastname", value = "Last name of user", required = false, dataType = "String", paramType = "query"),
#ApiImplicitParam(name = "phone", value = "phone number of user", required = false, dataType = "String", paramType = "query"),
#ApiImplicitParam(name = "signup", value = "Sign up date of user, in dd-MM-yyyy forma", required = false, dataType = "java.sql.Date", paramType = "query")
})
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RpcLogTemplate logTemplate = new RpcLogTemplate(req.getRemoteHost(),req.getParameter("client"), req.getParameter("clientapp"), Config.localhost, Config.SERVICE_INSTANCE, Config.SERVICE_APP, req.getParameterMap(), new Date().getTime() );
logger.debug("Received request: GET");
handleGet(req, resp, logTemplate);
logTemplate.setResponseTimestamp(new Date().getTime());
//LoggerService.INSTANCE.addLog(logTemplate);
}
private void handleGet(HttpServletRequest request, HttpServletResponse response, RpcLogTemplate logTemplate) throws IOException {
Gson gson = new Gson();
String param = null;
param = request.getParameter("id");
if(param!= null){
logger.info("Query by ID received. All other params would be ignored");
UserDatatype userDatatype = UserDao.INSTANCE.findById(param);
if(userDatatype == null){
response.setStatus(HttpServletResponse.SC_OK);
logger.info("Null object returned");
return;
}else{
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter printWriter = response.getWriter();
printWriter.write(gson.toJson(userDatatype, UserDatatype.class));
printWriter.flush();
printWriter.close();
}
}else{
Map<String, String> queryString = new HashMap<>();
//TODO: optimize this
param = request.getParameter("firstname");
if(param != null)
queryString.put("firstname", param);
param = request.getParameter("lastname");
if(param != null)
queryString.put("lastname", param);
param = request.getParameter("phone");
if(param != null)
queryString.put("phone", param);
param = request.getParameter("signup");
if(param != null)
queryString.put("signup", param);
UserDatatype[] userDatatypes = UserDao.INSTANCE.findByParams(queryString);
if(userDatatypes == null){
response.setStatus(HttpServletResponse.SC_OK);
logger.info("Null object returned");
return;
}else{
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter printWriter = response.getWriter();
printWriter.write(gson.toJson(userDatatypes, UserDatatype[].class));
printWriter.flush();
printWriter.close();
}
}
}
}
Bootstrap.java
public class Bootstrap extends HttpServlet {
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.2");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:7000");
beanConfig.setBasePath("/");
beanConfig.setResourcePackage("io.swagger.resources");
beanConfig.setScan(true);
beanConfig.setPrettyPrint(true);
}
}
All help appreciated.
EDIT: Quick update. After modifying the initializeAPI method to the original Servlet class in the blog(see below), I was able to get some response from the swagger-ui on http://host:7000/api/swagger.json. But I it seems like swagger wasnt able to parse my servlet annotations
http://host:7000/api/swagger.json ===> {"swagger":"2.0","info":{"version":"1.0.0","title":""}}
UPDATED CODE
private static Server initializeApi(Properties properties) {
logger.info("Initializing user profile server...");
new UserDao();
Server server = new Server(Integer.parseInt(properties.getProperty(Config.JETTY_SERVICE_PORT)));
ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContextHandler.setContextPath("/");
server.setHandler(servletContextHandler);
//Setup APIs
ServletHolder apiservlet = servletContextHandler.addServlet(ServletContainer.class, "/api/*");
apiservlet.setInitOrder(1);
apiservlet.setInitParameter("com.sun.jersey.config.property.packages", "com.api.resources;io.swagger.jaxrs.json;io.swagger.jaxrs.listing");
apiservlet = servletContextHandler.addServlet(ProfileServlet.class, "/user/*");
//apiservlet.setInitOrder(1);
apiservlet.setInitParameter("com.sun.jersey.config.property.packages", "com.api.resources;io.swagger.jaxrs.json;io.swagger.jaxrs.listing");
logger.info("User profile server initialized.");
// Setup Swagger servlet
ServletHolder swaggerServlet = servletContextHandler.addServlet(DefaultJaxrsConfig.class, "/swagger-core");
swaggerServlet.setInitOrder(2);
swaggerServlet.setInitParameter("api.version", "1.0.0");
// Setup Swagger-UI static resources
String resourceBasePath = Main.class.getResource("/webapp").toExternalForm();
servletContextHandler.setWelcomeFiles(new String[] {"index.html"});
servletContextHandler.setResourceBase(resourceBasePath);
servletContextHandler.addServlet(new ServletHolder(new DefaultServlet()), "/*");
return server;
}
So thanks to the Swagger google group who figured out what was wrong with my code. Two things:
1.You have to add your package (the one that contains the servlets as an initialization parameter to the ServletContainer class. So under Main.class in the initializeApi() method, I changed it from:
//Setup APIs
ServletHolder apiservlet = servletContextHandler.addServlet(ServletContainer.class, "/api/*");
apiservlet.setInitOrder(1); apiservlet.setInitParameter("com.sun.jersey.config.property.packages", "com.api.resources;io.swagger.jaxrs.json;io.swagger.jaxrs.listing");
to this:
//Setup APIs
ServletHolder apiservlet = servletContextHandler.addServlet(ServletContainer.class, "/api/*");
apiservlet.setInitOrder(1);
apiservlet.setInitParameter("com.sun.jersey.config.property.packages", "com.api.resources;io.swagger.jaxrs.json;io.swagger.jaxrs.listing; {path/to/package}");
In my case {path/to/package} was com.coreservice.servlets
2.Add your package name as a resource to the Swagger initialization servlet. In my project, it was the Bootstrap.java class. So my new Bootstrap.java class looks like this
public class Bootstrap extends HttpServlet {
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.2");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:7000");
beanConfig.setBasePath("/");
beanConfig.setResourcePackage("com.coreservice.servlets");
beanConfig.setScan(true);
beanConfig.setPrettyPrint(true);
}
Related
I'm working to migrate from Eclipse Jersey/Grizzly (2.33) to Eclipse/Jetty (10.0.6) for the embedded Http Server for our REST API, and I can't for the life of me get the multipart/form-data uploads working properly. I freely admit I am not versed in Jetty configuration, nor Jersey/Grizzly configuration, and I'm cobbling together the old code with the bare minimum of boilerplate from Jetty cookbooks.
At this point, I'd be thrilled to just get the server to accept the request. I can work on how to handle the files on my own. My primary goal at the moment is to not have to rewrite dozens of servlets/handlers right now (hence the use of the Jersey ServletContainer).
This is the server code:
public static void start() throws Exception {
httpConfig = new HttpConfiguration();
HttpConnectionFactory http11 = new HttpConnectionFactory(httpConfig);
server = new Server();
ServerConnector connector = new ServerConnector(server, http11);
connector.setPort((cmdOptions.port < 0 ? 9998 : cmdOptions.port));
server.setConnectors(new Connector[] {connector});
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
HandlerList handlers = new HandlerList();
ServletHandler servletHandler = new ServletHandler();
// Set up the resources.common package as the handlers for the servlet
ServletHolder servletHolder = context.addServlet(ServletContainer.class, "/*");
servletHolder.setInitOrder(0);
servletHolder.setInitParameter("jersey.config.server.provider.packages", "resources.grizzly;resources.common");
servletHandler.addServlet(servletHolder);
// MultiPartConfig setup - to allow for ServletRequest.getParts() usage
Path multipartTmpDir = Paths.get("target", "multipart-tmp");
multipartTmpDir = CommonResFileManager.ensureDirExists(multipartTmpDir);
String location = multipartTmpDir.toString();
long maxFileSize = 10 * 1024 * 1024; // 10 MB
long maxRequestSize = 10 * 1024 * 1024; // 10 MB
int fileSizeThreshold = 64 * 1024; // 64 KB
MultipartConfigElement multipartConfig = new MultipartConfigElement(location, maxFileSize, maxRequestSize, fileSizeThreshold);
FilterHolder filterHolder;
filterHolder = context.addFilter(resources.jetty.SecurityFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
filterHolder.setAsyncSupported(true);
CorsHandler corsHandler = new CorsHandler();
corsHandler.setHandler(context);
UploadHandler uploadHandler = new UploadHandler("/G/uploadFolder", multipartConfig, multipartTmpDir);
handlers.addHandler(corsHandler);
handlers.addHandler(uploadHandler);
handlers.addHandler(servletHandler);
server.setHandler(handlers);
server.start();
}
The resources of interest are:
public class CommonResProject extends CommonResBase {
...
#POST #Path("uploadFolder")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.MULTIPART_FORM_DATA)
public String uploadFolder(final FormDataMultiPart multiPart)
{
Collection<Part> parts = null;
try {
parts = ((HttpServletRequest)request).getParts();
} catch (IOException | ServletException ex) {
Logger.getLogger(CommonResProject.class.getName()).log(Level.SEVERE, null, ex);
}
if(parts != null){
parts.stream().forEach(p -> System.out.println(p.getName() + " ["+p.getContentType()+"]: "+p.getSize()+" bytes"));
}
// projects is a POJO that actually does the fiddly bits with the uploaded files
boolean retVal = projects.uploadFolder(getDB(), getUserId(), multiPart);
return "{\"retVal\" : " + String.valueOf(retVal) + "}";
}
...
Which is extended by:
#Path("/GProject")
public class GProject extends CommonResProject
{
public GProject()
{
super();
resInterface = new GBaseRes(); // Must always do
}
public static void processParts(HttpServletRequest request, HttpServletResponse response, java.nio.file.Path outputDir) throws ServletException, IOException
{
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
for (Part part : request.getParts())
{
out.printf("Got Part[%s].size=%s%n", part.getName(), part.getSize());
out.printf("Got Part[%s].contentType=%s%n", part.getName(), part.getContentType());
out.printf("Got Part[%s].submittedFileName=%s%n", part.getName(), part.getSubmittedFileName());
String filename = part.getSubmittedFileName();
if (StringUtil.isNotBlank(filename))
{
// ensure we don't have "/" and ".." in the raw form.
filename = URLEncoder.encode(filename, "utf-8");
java.nio.file.Path outputFile = outputDir.resolve(filename);
try (InputStream inputStream = part.getInputStream();
OutputStream outputStream = Files.newOutputStream(outputFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))
{
IO.copy(inputStream, outputStream);
out.printf("Saved Part[%s] to %s%n", part.getName(), outputFile.toString());
}
}
}
}
public static ServletContextHandler newServletUploadHandler(MultipartConfigElement multipartConfig, java.nio.file.Path outputDir) throws IOException
{
ServletContextHandler context = new ServletContextHandler();
SaveUploadServlet saveUploadServlet = new SaveUploadServlet(outputDir);
ServletHolder servletHolder = new ServletHolder(saveUploadServlet);
servletHolder.getRegistration().setMultipartConfig(multipartConfig);
context.addServlet(servletHolder, "/uploadFolder");
return context;
}
public static class SaveUploadServlet extends HttpServlet
{
private final java.nio.file.Path outputDir;
public SaveUploadServlet(java.nio.file.Path outputDir) throws IOException
{
this.outputDir = outputDir.resolve("servlet");
ensureDirExists(this.outputDir);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
processParts(request, response, outputDir);
}
}
public static class UploadHandler extends AbstractHandler
{
private final String contextPath;
private final MultipartConfigElement multipartConfig;
private final java.nio.file.Path outputDir;
public UploadHandler(String contextPath, MultipartConfigElement multipartConfig, java.nio.file.Path outputDir) throws IOException
{
super();
this.contextPath = contextPath;
this.multipartConfig = multipartConfig;
this.outputDir = outputDir.resolve("handler");
CommonResFileManager.ensureDirExists(this.outputDir);
}
#Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (!target.startsWith(contextPath))
{
// not meant for us, skip it.
return;
}
if (!request.getMethod().equalsIgnoreCase("POST"))
{
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return;
}
// Ensure request knows about MultiPartConfigElement setup.
request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, multipartConfig);
// Process the request
processParts(request, response, outputDir);
//baseRequest.setHandled(true);
}
}
}
And the whole thing generates the following stacktrace when I try to upload a set of files:
2021-09-13 12:58:17 SEVERE - resources.common.ResponseExceptionMapper toResponse -- HTTP 415 Unsupported Media Type
javax.ws.rs.NotSupportedException: HTTP 415 Unsupported Media Type
at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:75)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker.getParamValues(JavaResourceMethodDispatcherProvider.java:109)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:219)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:79)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:475)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:397)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)
at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:255)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:234)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:680)
at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:366)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:319)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1619)
at resources.jetty.SecurityFilter.doFilter(SecurityFilter.java:232)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
at org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:164)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:506)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1571)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1372)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:463)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1544)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1294)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at resources.jetty.CorsHandler.handle(CorsHandler.java:30)
at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:51)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.server.Server.handle(Server.java:562)
at org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:406)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:663)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:398)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:319)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
at org.eclipse.jetty.io.SocketChannelEndPoint$1.run(SocketChannelEndPoint.java:101)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:412)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:381)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:268)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new$0(AdaptiveExecutionStrategy.java:138)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:378)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:894)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1038)
at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=multipart/form-data;boundary=----WebKitFormBoundary4K5nPFIDDwLPZAnk, type=class org.glassfish.jersey.media.multipart.FormDataMultiPart, genericType=class org.glassfish.jersey.media.multipart.FormDataMultiPart.
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:208)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:132)
at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundReadFrom(MappableExceptionWrapperInterceptor.java:49)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:132)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1072)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:885)
at org.glassfish.jersey.server.ContainerRequest.readEntity(ContainerRequest.java:282)
at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:73)
at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:56)
at org.glassfish.jersey.server.spi.internal.ParamValueFactoryWithSource.apply(ParamValueFactoryWithSource.java:50)
at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:68)
First, don't use ServletHandler directly like that.
Only use ServletContetHandler and ServletHolder to configure what you need.
ServletHandler is an internal class not meant to be used directly like that.
Especially with all of the configuration you are attempting to force on it.
Next, convert UploadHandler to a normal/formal HttpServlet and add it to the ServletContextHandler properly (you can even use the same url-pattern as you are currently). The ServletContext is important here (for multipart), and your raw/naked UploadHandler is not actually handling multipart like you think it is.
The stacktrace indicates that you are not using Jetty for multipart at the point in time where the stacktrace is generated, which means it bypassed the UploadHandler and Jersey itself is attempting to handle the multipart content. This probably means you have the specify the MultiPartConfigElement on the Jersey servlet instead.
ServletHolder servletHolder = context.addServlet(ServletContainer.class, "/*");
servletHolder.setInitOrder(0);
servletHolder.setInitParameter("jersey.config.server.provider.packages",
"resources.grizzly;resources.common");
Path multipartTmpDir = Paths.get("target", "multipart-tmp");
multipartTmpDir = CommonResFileManager.ensureDirExists(multipartTmpDir);
String location = multipartTmpDir.toString();
long maxFileSize = 10 * 1024 * 1024; // 10 MB
long maxRequestSize = 10 * 1024 * 1024; // 10 MB
int fileSizeThreshold = 64 * 1024; // 64 KB
MultipartConfigElement multipartConfig = new MultipartConfigElement(location,
maxFileSize, maxRequestSize, fileSizeThreshold);
servletHolder.getRegistration().setMultipartConfig(multipartConfig);
Is it in some way possible that an exception thrown from a rest service is returned as JSON? I have a JAX-RS Rest Service where I would like to achieve this. Basically, I am returning a zip file as a Response from our API which is working as expected but when I am getting any exception the same I want to return it as a JSON response from API. Is there any way to achieve this:
#RolesAllowed({"admin", "analyst", "investigator", "supervisor", "data_scientist", "quality_assurance", "executive", "confidential"})
#Path("/download/{id}")
#GET
#Produces("application/zip")
#Consumes(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Returns a List.", notes = "")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "", response = VO.class),
#ApiResponse(code = 404, message = "", response = ErrorMessage.class),
#ApiResponse(code = 500, message = "", response = ErrorMessage.class) })
public Response downloadFile(
#HeaderParam("x-access-token") String jwtToken,
#Context SecurityContext sec,
#Context final HttpServletResponse response,
#PathParam("id") String id)
throws Exception {
ErrorMessage errorMessage=new ErrorMessage();
ResponseBuilder response1=null;
try{
if (id == null) {
throw new BusinessException("Id should not be empty.Please provide Id.");
}
String File=DownloadUtil.exportToFile(wl);
byte[] bytes =Files.readAllBytes(Paths.get(File));
ByteArrayOutputStream out = new ByteArrayOutputStream();
ZipOutputStream zipOut = new ZipOutputStream( out );
zipOut.putNextEntry( new ZipEntry( File ) );
zipOut.write(bytes);
zipOut.closeEntry();
zipOut.close();
response1 = Response.ok(out.toByteArray());
response1.header("Content-Disposition", "attachment; filename=list.zip");
}
catch (Exception msg)
{
errorMessage.setCode(422);//your custom error code
errorMessage.setMessage("Error occured");//custom status
return Response.status(200).entity(errorMessage).build();
}
return response1.build();
}
Using the above error message technique I am getting the following exception and in the response getting [Object Blob]:
No message body writer has been found for class com.test.exception.ErrorMessage, ContentType: application/zip
I send form data in POST request from angular app to my azure functions who wrriten in java.
the client side look like this:
#Injectable({
providedIn: 'root'
})
export class SendItemToAzureFunctionsService {
private functionURI: string;
constructor(private http: HttpClient) {
this.functionURI = 'https://newsfunctions.azurewebsites.net/api/HttpTrigger-Java?code=k6e/VlXltNs7CmJBu7lmBbzaY4tlo21lXaLuvfG/tI7m/XXXX';
}
// {responseType: 'text'}
sendItem(item: Item){
let body = new FormData();
body.append('title', item.title);
body.append('description', item.description);
body.append('link', item.link);
return this.http.post(this.functionURI, body)
.pipe(
map((data: string) => {
return data;
}), catchError( error => {
return throwError( 'Something went wrong!' );
})
)
}
}
when Item recived to azure functions.
the aim of functions is to send this item in push notifications via firebase to android app.
the azure functions with HTTP trigger look like this:
#FunctionName("HttpTrigger-Java")
public HttpResponseMessage run(#HttpTrigger(name = "req", methods = { HttpMethod.GET,
HttpMethod.POST }, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) {
context.getLogger().info("Java HTTP trigger processed a request.");
// Parse query parameter
String itemDetails = request.getBody().get();
if (itemDetails == null) {
return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
.body("Please pass a name on the query string or in the request body").build();
} else {
// ======
String postUrl = "https://fcm.googleapis.com/fcm/send";
HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(postUrl);
post.setHeader("authorization", FIREBAE_AUTH);
post.setHeader("Content-type", "application/json");
JSONObject contentJson = new JSONObject();
contentJson.put("title", "example title");
contentJson.put("description", "example text");
JSONObject pushNotificationJson = new JSONObject();
pushNotificationJson.put("data", contentJson);
pushNotificationJson.put("to", "/topics/newsUpdateTopic");
try {
StringEntity stringEntity = new StringEntity(pushNotificationJson.toString(), "UTF-8");
post.setEntity(stringEntity);
HttpResponse response = httpClient.execute(post);
System.out.println(response.getEntity().getContent().toString());
} catch (IOException var9) {
var9.printStackTrace();
}
// =========
}
return request.createResponseBuilder(HttpStatus.OK)
.body("succeed to send new item in push notification to clients").build();
}
when I am running String itemDetails = request.getBody().get();
I am getting:
------WebKitFormBoundary2gNlxQx5pqyAeDL3
Content-Disposition: form-data; ....
I will be glad to know how to get data item from that?
If you want to parse from-date type data in Azure function with java, you can try to use MultipartStream in SDK org.apache.commons.fileupload to implement it. For example
code
public HttpResponseMessage run(
#HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) throws IOException {
context.getLogger().info("Java HTTP trigger processed a request.");
String contentType = request.getHeaders().get("content-type");
String body = request.getBody().get(); // Get request body
String boundary = contentType.split(";")[1].split("=")[1]; // Get boundary from content-type header
int bufSize = 1024;
InputStream in = new ByteArrayInputStream(body.getBytes()); // Convert body to an input stream
MultipartStream multipartStream = new MultipartStream(in, boundary.getBytes(), bufSize, null); // Using MultipartStream to parse body input stream
boolean nextPart = multipartStream.skipPreamble();
while (nextPart) {
String header = multipartStream.readHeaders();
int start =header.indexOf("name=") + "name=".length()+1;
int end = header.indexOf("\r\n")-1;
String name = header.substring(start, end);
System.out.println(name);
multipartStream.readBodyData(System.out);
System.out.println("");
nextPart = multipartStream.readBoundary();
}
return request.createResponseBuilder(HttpStatus.OK).body("success").build();
}
Test. I test with postman
I've used #Jim Xu's code and created a class to get the data in easier way. Here is the gist - https://gist.github.com/musa-pro/dcef0bc23e48227e4b89f6e2095f7c1e
The jaxrs-jersey code generator creates stubs along these lines:
#Path("/pet")
#io.swagger.annotations.Api(description = "the pet API")
public class PetApi {
private final PetApiService delegate;
public PetApi(#Context ServletConfig servletContext) {
PetApiService delegate = null;
if (servletContext != null) {
String implClass = servletContext.getInitParameter("PetApi.implementation");
if (implClass != null && !"".equals(implClass.trim())) {
try {
delegate = (PetApiService) Class.forName(implClass).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
if (delegate == null) {
delegate = PetApiServiceFactory.getPetApi();
}
this.delegate = delegate;
}
#POST
#Consumes({ "application/json", "application/xml" })
#Produces({ "application/xml", "application/json" })
#io.swagger.annotations.ApiOperation(value = "Add a new pet to the store", notes = "", response = Void.class, authorizations = {
#io.swagger.annotations.Authorization(value = "petstore_auth", scopes = {
#io.swagger.annotations.AuthorizationScope(scope = "write:pets", description = "modify pets in your account"),
#io.swagger.annotations.AuthorizationScope(scope = "read:pets", description = "read your pets")
})
}, tags={ "pet", })
#io.swagger.annotations.ApiResponses(value = {
#io.swagger.annotations.ApiResponse(code = 405, message = "Invalid input", response = Void.class) })
public Response addPet(#ApiParam(value = "Pet object that needs to be added to the store" ,required=true) Pet body
,#Context SecurityContext securityContext)
...where only SecurityContext is injected.
Is there a way to influence code generation to inject other context state, or a way to pass an instance instead of class with blank instantiation?
Example source
What am I trying to do?
I am trying to generate new timestamped tokens from server side that client can use in their subsequent request
What I have tried?
I have a Servlet filter which wraps around REST calls and looks like
#WebFilter(urlPatterns = "/rest/secure")
public class SecurityFilter implements Filter {
private static final Pattern PATTERN = Pattern.compile(":");
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityFilter.class);
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
//LOGGER.info("initializing SecurityFilter");
}
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
final String authToken = getAuthenticationHeaderValue((HttpServletRequest) request);
try {
validateAuthToken(authToken);
} catch (IllegalArgumentException tokenNotValidException) {
LOGGER.error("invalid token");
httpServletResponse.sendError(401);
}
try {
chain.doFilter(request, response);
} catch (Exception e) {
LOGGER.error("exception: " + e.getMessage());
}finally {
final String newAuthToken = generateNewAuthToken(authToken);
httpServletResponse.addHeader(AUTH_TOKEN, newAuthToken);
LOGGER.info("added new security token: " + newAuthToken);
}
}
and in one of my endpoints I do
#PUT
public Response updateUser() {
throw new IllegalArgumentException("just for test purposes");
}
I am using RESTEasy for all REST based work.
and I am also using Seam REST library to map server exceptions to REST based exceptions
#ExceptionMapping.List({
#ExceptionMapping(exceptionType = IllegalArgumentException.class, status = 400, useExceptionMessage = true),
#ExceptionMapping(exceptionType = PersistenceException.class, status = 400, useExceptionMessage = true),
#ExceptionMapping(exceptionType = ConstraintViolationException.class, status = 400, useExceptionMessage = true),
#ExceptionMapping(exceptionType = ValidationException.class, status = 400, useExceptionMessage = true),
#ExceptionMapping(exceptionType = NoResultException.class, status = 404, useExceptionMessage = true),
#ExceptionMapping(exceptionType = IllegalStateException.class, status = 406, useExceptionMessage = true),
#ExceptionMapping(exceptionType = NoClassDefFoundError.class, status = 404, useExceptionMessage = true),
#ExceptionMapping(exceptionType = UnsupportedOperationException.class, status = 400, useExceptionMessage = true),
})
#ApplicationPath("/rest")
public class MarketApplicationConfiguration extends Application {
}
Problem?
- when endpoint throws the exception, the callback is never returned to the filter code.
- This is even when I use try/catch/finally as follows
try {
chain.doFilter(request, response);
} catch (Exception e) {
LOGGER.error("exception: " + e.getMessage());
}finally {
final String newAuthToken = generateNewAuthToken(authToken);
httpServletResponse.addHeader(AUTH_TOKEN, newAuthToken);
LOGGER.info("added new security token: " + newAuthToken);
}
- I can however test that the IllegalArgumentException is mapped to HTTP 400 based on Seam REST exception mapping, but it is never returned back to the SecurityFilter code in case of server exceptions.
Required?
- I want to generate server tokens even when the application throws exception(s) so that client can use them
- How can, in case of exceptions, I can route my response through SecurityFilter?
i think you should use for this purpose own exceptions handler which can be defined in web.xml and in case of exception you should process it inside exception handler and not in filter.
you can get more details in article "Servlets - Exception Handling"