3

I've seen a Java RESTFUL webservice, that allowed the content-type to be requested in the URL with an extension at the end, such as

  • .xml
  • .json

This is the style of content negotiation I am striving to achieve in my own Web Service.

I am aware of the @Produces annotation, and the fact a method can resolve multiple types with the (value = {}) syntax, by adding an Accept header, say with Postman, the Chrome extension.

But I'm not sure how to effectively extract out the information in one method, and delegate to another method.

I'm assuming REGEX's can be use with @Path and @PathParam, but my attempts to do this have yet to be fruitful.

Can anyone provide an example?


This is my attempt thus far:

package com.extratechnology.caaews;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.extratechnology.caaews.model.Log;


@Path("et")
@Produces(MediaType.APPLICATION_JSON)
public class CAAEWS {


    @GET
    @Path("\\.{format}")
    @Produces(value = {MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
    public Log getLog(
            @PathParam("format") String format
    ){
        Log result = null;
        switch (format) {
        case "json":
            result = this.getJSON();
        case "xml":
            result = this.getXML();
        }
        return result;
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Log getJSON() {
        return new Log("JSON!");
    }

    @GET
    @Produces(MediaType.TEXT_XML)
    public Log getXML() {
        return new Log("XML!");
    }

}

package com.extratechnology.caaews.model;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Log {
    private String log;

    public Log(String log) {
        this.log = log;
    }

    public String getLog() {
        return log;
    }

    public void setLog(String log) {
        this.log = log;
    }

}

The project can be setup from Spring Tool Suite/Eclipse, by creating a Maven project (similar, but more up to date than here circa 4:50) using the following:

  • org.glassfish.jersey.archetypes
  • jersey.quickstart.webapp
  • 2.26

Then you uncomment the part of the pom.xml provided to enable JSON support, which effectively adds a few more JARS to your WAR.

I found I had some nasty BCEL errors too, and had to append some entries to the catalina.properties file, under the key:

tomcat.util.scan.StandardJarScanFilter.jarsToSkip=\
....
javax.json-api-1.1.jar, javax.json.bind-api-1.0.jar, javax.json-1.1.jar, \
yasson-1.0.jar

http://localhost:18080/caaews/webapi/et

yields:

{"log":"JSON!"}

http://localhost:18080/caaews/webapi/et.xml or

http://localhost:18080/caaews/webapi/et.json

yields:

HTTP Status 404 - Not Found

I'm also wondering if there's some sort of HTTP Interceptor type approach to this. My Java is a bit rusty, but is it servlet filters, or something akin to an AOP before advice.


Thanks to @user1803551 I have put breaks in switch statements.

Thanks to @callmepills I have tweaked code a bit.

The class level @Path annotation now has this. @Produces(value = {MediaType.APPLICATION_JSON, MediaType.TEXT_XML})

The getLog @Path annotation is ".{format}".

In order to have the getLog be called and delegate, you have to use this syntax for the URL:

http://localhost:18080/caaews/webapi/et

http://localhost:18080/caaews/webapi/et/.xml

http://localhost:18080/caaews/webapi/et/.json

The need to have a '/' in the path isn't what I'm after, so I think I will probably have to resolve to servlet filters rather than @PathParam approach..

JGFMK
  • 8,425
  • 4
  • 58
  • 92
  • Sorry for the off topic, but IMHO... support for both xml and json is usually a waste of time. Moreover, using some kind of suffix in the URL for this purpose is even bigger mistake. – G. Demecki Nov 24 '17 at 20:27
  • Provide an example input and your attempt(s). See [mcve]. This site is not a coding service. – user1803551 Nov 24 '17 at 20:29
  • @user1803551 - You didn't give me chance! Was going to anyhow! – JGFMK Nov 24 '17 at 20:56
  • Didn't give you a chance? You're supposed to have the question ready before you post it, not continuously add details to it with edits. – user1803551 Nov 24 '17 at 20:58
  • 1
    **Caution:** `case` statements without `break`. Enable standard Java compiler warnings, and heed them, and you won’t make this mistake again. – VGR Nov 24 '17 at 22:09
  • @VGR - You are indeed correct. I originally tried multiple returns, and refactored code, and forgot to insert the breaks. Thanks for the keen eye. But, in retrospect it would just impact performance a miniscule amount, with code falling thru and not matching second condition. – JGFMK Nov 25 '17 at 10:45

4 Answers4

1

There are several problems with your JAX-RS code:

Regex in @Path

The value of the @Path annotation parses regex only inside a parameter template and after a : character. You're trying to use a regex outside of the parameter template, "\\.{format}", so it's not parsing it as a regex.

Path resolution

A method's path includes the segment of the class path followed by its own path segment. Your code suggests the paths /et/.{format} and /et while you're trying to call /et.{format}, which isn't defined anywhere, hence the 404.


Here is an example that would work in accordance with your code:

@Path("et")
public class Resource {

    private static final String JSON = "json";
    private static final String XML = "xml";

    @GET
    @Path(".{format:(" + JSON + "|" + XML + ")}")
    @Produces(value = { MediaType.APPLICATION_JSON, MediaType.TEXT_XML }) // not XML?
    public String getLog(@PathParam("format") String format) {
        switch (format) {
            case JSON:
                this.getJSON();
                break;
            case XML:
                this.getXML();
        }
        return format;
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public void getJSON() {
        System.out.println("in JSON");
    }

    @GET
    @Path("otherPath")
    @Produces(MediaType.APPLICATION_XML)
    public void getXML() {
        System.out.println("in XML");
    }
}

Your valid requests will now be:

Change the paths according to what you want. I used "otherPath" for the XML method because it can't conflict with the empty path JSON method. I do not recommend this convention.

Notes:

  • Use a break in the switch statement.
  • To reduce the chances for bugs, use constants for reusable strings and the like as I did with your custom format types. An enum would be even better.

Edit:

The request is now to have a path /et/<something>.{format}. That can be achieved if we expand the scope of the path parameter to include the whole segment <something>.{format} and then programmatically extract the format:

@GET
@Path("{segment:[a-zA-Z0-9_]*\\.(" + JSON + "|" + XML + ")}")
@Produces(value = { MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
public String getLog(@PathParam("segment") String segment) {
    String format = segment.substring(segment.indexOf('.') + 1);
    switch (format) {
        case JSON:
            this.getJSON();
            break;
        case XML:
            this.getXML();
    }
    return format;
}

The regex [a-zA-Z0-9_]* means any alphanumeric or underscore once or more. You can replace that part with whatever restriction you want. Consult the URL specifications for allowed characters.

user1803551
  • 12,965
  • 5
  • 47
  • 74
  • Thanks for Regex comment. I'm not sure off the top of my head, about the way to do 'ends with' in a regex format. I would imagine it's something involving caret or dollar symbol, and escaping the dot. It was on my to do list to experiment with today. I also added comment about possible servlet filter, so effectively it could be used on all web service calls and coded in a single place. – JGFMK Nov 25 '17 at 10:38
  • @JGFMK See my edit. Next time please clearly state the desired result. – user1803551 Nov 25 '17 at 12:48
  • Hello! Quote: "I've seen a Java RESTFUL webservice, that allowed the content-type to be requested in the URL with an extension at the end, such as .xml .json" – JGFMK Nov 25 '17 at 13:32
  • @JGFMK That isn't specific, my initial code does that and it wasn't what you wanted exactly. See [ask] and [mcve]. In any case, now the answer solves the problem you posted. – user1803551 Nov 25 '17 at 13:39
  • 2
    Excellent answer, except that you’re wrong about Produces/Consumes. He is not sending any body; the resource method can *produce* either XML or JSON. `@Produces` is in fact the correct annotation here. – VGR Nov 25 '17 at 16:20
  • @VGR I don't understand, his methods consume and produce things, but the data sent back is not the question here, it's the data the client sends to the server. That is controlled by `@Consumes`. – user1803551 Nov 25 '17 at 23:07
  • The request body is ignored for GET requests hence nothing to consume. – callmepills Nov 26 '17 at 00:19
  • I see no evidence that the client is sending any content to the server. The resource method clearly does not expect any XML content or JSON content; it is only returning XML or JSON content. – VGR Nov 26 '17 at 02:23
  • @VGR OK, I changed it back. – user1803551 Nov 26 '17 at 11:51
  • @callmepills OK, I changed it back. – user1803551 Nov 26 '17 at 11:52
1

Even though you didn't tag , your question shows you are using Jersey, so I am going to post a Jersey-specific solution. What Jersey offers is a property that you can use to set the media type mappings

ServerPropeties.MEDIA_TYPE_MAPPINGS

public static final String MEDIA_TYPE_MAPPINGS

Defines mapping of URI extensions to media types. The property is used by UriConnegFilter. See it's javadoc for more information on media type mappings.

The property value MUST be an instance of String, String[] or Map<String, MediaType>. Each String instance represents one or more uri-extension-to-media-type map entries separated by a comma (","). Each map entry is a key-value pair separated by a colon (":"). Here is an example of an acceptable String value mapping txt extension to text/plain and xml extension to application/xml:

txt : text/plain, xml : application/xml

A default value is not set.

The name of the configuration property is "jersey.config.server.mediaTypeMappings".

Example with Java config

final Map<String, MediaType> mediaTypeMappings = new HashMap<>();
mediaTypeMappings.put("xml", MediaType.APPLICATION_XML_TYPE);
mediaTypeMappings.put("json", MediaType.APPLICATION_JSON_TYPE);

final ResourceConfig rc = new ResourceConfig()
        .packages("com.example.jersey")
        .property(ServerProperties.MEDIA_TYPE_MAPPINGS, mediaTypeMappings);

Example with web.xml config

<servlet>
    <servlet-name>JerseyApplication</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>com.example</param-value>
    </init-param>
    <init-param>
        <param-name>jersey.config.server.mediaTypeMappings</param-name>
        <param-value>xml:application/xml, json:application/json</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • This is a neater solution than my approach, but for some reason it yields similar HTTP 500 errors: `0:0:0:0:0:0:0:1 - - [26/Nov/2017:07:57:12 +0000] "GET /caaews/webapi/et HTTP/1.1" 500 1082 0:0:0:0:0:0:0:1 - - [26/Nov/2017:07:58:26 +0000] "GET /caaews/webapi/et.xml HTTP/1.1" 500 1082 0:0:0:0:0:0:0:1 - - [26/Nov/2017:07:58:39 +0000] "GET /caaews/webapi/et.json HTTP/1.1" 200 15` ie. only .JSON works... – JGFMK Nov 26 '17 at 08:05
  • I also tried consolidating the getXML and getJSON into a single getLog method, rather than having one of each, and at the method level have the dual @Produces annotation. But that didn't make a difference either. – JGFMK Nov 26 '17 at 08:11
  • I'm trying to get better error handling integrated into solution. I'm looking at these threads for anyone who's following in my footsteps.. https://stackoverflow.com/questions/31289470/http-500-internal-server-error-in-simple-rest-based-program-confused-in-get-and https://stackoverflow.com/questions/25420697/jersey-2-exception-handing https://stackoverflow.com/questions/31501110/registering-a-provider-programmatically-in-jersey-which-implements-exceptionmapp – JGFMK Nov 26 '17 at 08:32
  • 1
    You need a no-arg constructor in your Log class. This is needed for XML with JAXB. You're probably getting an error that's getting swallowed up – Paul Samsotha Nov 26 '17 at 09:10
  • That was indeed correct. That fixed it. I guess my only remaining question is the 'default' is xml, how do I change it to JSON? – JGFMK Nov 26 '17 at 09:54
  • https://dzone.com/articles/content-negotiation-in-jax-rs-20 -'qs'. Will give it a try – JGFMK Nov 26 '17 at 10:00
  • Can't seem to get qs to work.. Also docs here.. https://jersey.github.io/documentation/latest/jaxrs-resources.html#d0e2167 – JGFMK Nov 26 '17 at 10:20
  • Also, changing sequence in annotaqtion per jersey docs doesn't make an iota of difference. Ditto changing in web.xml. Always defaults to xml for me. – JGFMK Nov 26 '17 at 10:24
0

Have you tried getting rid of the @Path annotation at the class level? Your method level annotation would then be:

@Path("et.{format}")

I think your current implementation is creating a sub-resource which matches a path like:

/et/{format}
callmepills
  • 672
  • 9
  • 13
  • You may be right about the sub-resource... But when I tried this, and used @Path("et") on both the getXML and getJSON methods, I suddenly got 404's across the board... – JGFMK Nov 25 '17 at 11:05
  • I also noticed I never gave dual content types at the class level on the @Produces. When I did that. it did delegate correctly if I used the format. http://localhost:18080/caaews/webapi/et/.json or http://localhost:18080/caaews/webapi/et/.xml. So that explains why the code wasn't working (I also removed the \\ in the path and just used ".{format}". SO I guess the @Path/@PathParam approach isn't what I need after all to have the extension style of URL. I will look more into servlet filters. – JGFMK Nov 25 '17 at 11:17
  • Why do you need separate methods? Assuming **Log** is both marshallable and serializable, you should only need to update the `@Produces` annotation based on what you support (e.g. application/json, application/xml, text/xml, etc). – callmepills Nov 26 '17 at 01:09
  • If separate methods are necessary, then why not explicitly define the paths for each `@Path("et.xml")` and `@Path("et.json")`? – callmepills Nov 26 '17 at 01:12
-1

When I Googled on "servlet filter with jax-rs example", this was at the top of the list. From a cursory scan at the code, I think this suits my needs.


Here is my solution (so far... see footnote caveat)

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
     see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

  <filter>
        <filter-name>accept-filter</filter-name>
        <filter-class>com.extratechnology.filters.AcceptFilter</filter-class>
        <init-param>
            <param-name>xml</param-name>
            <param-value>text/xml</param-value>
        </init-param>
        <init-param>
            <param-name>json</param-name>
            <param-value>application/json</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>accept-filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.extratechnology.caaews</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/webapi/*</url-pattern>
    </servlet-mapping>
</web-app>

AcceptFilter.java

package com.extratechnology.filters;
import java.io.IOException;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class AcceptFilter implements Filter  {
    private final Map<String,String> extensions = new HashMap<String,String>();

    public void init(FilterConfig config) throws ServletException {
        Enumeration<String> exts = config.getInitParameterNames();
        while (exts.hasMoreElements()) {
            String ext = exts.nextElement();
            if (ext != null && !ext.isEmpty()) {
                this.extensions.put(ext.toLowerCase(), config.getInitParameter(ext));
            }
        }
    }

    public void destroy() {}

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest)request;
        String uri = httpRequest.getRequestURI();
        String ext = this.getExtension(uri);
        String accept = this.extensions.get(ext);

        if (accept == null) {
            accept = httpRequest.getHeader("accept");
            if (accept != null && accept.indexOf("text/html") > 0) {
                // patch WebKit-style Accept headers by elevating "text/html"
                accept = "text/html,"+accept;
                request = new RequestWrapper(httpRequest, uri, accept);
            }
        } else {
            // remove extension and remap the Accept header
            uri = uri.substring(0, uri.length() - ext.length()-1);
            request = new RequestWrapper(httpRequest, uri, accept);
        }

        // add "Vary: accept" to the response headers
        HttpServletResponse httpResponse = (HttpServletResponse)response;
        httpResponse.addHeader("Vary", "accept");

        chain.doFilter(request, response);
    }

    private String getExtension(String path) {
        String result = "";
        int index = path.lastIndexOf('.');
        if (!(index < 0 || path.lastIndexOf('/') > index)) {
            result =  path.substring(index+1).toLowerCase();
        }
        return result;
    }

    private static class RequestWrapper extends HttpServletRequestWrapper {

        private final String uri;
        private final String accept;

        public RequestWrapper(HttpServletRequest request, String uri, String accept) {
            super(request);

            this.uri = uri;
            this.accept = accept;
        }

        @Override
        public String getRequestURI() {
            return this.uri;
        }
        @Override
        public Enumeration<String> getHeaders(String name) {
            Enumeration<String> result;
            if ("accept".equalsIgnoreCase(name)) {
                Vector<String> values = new Vector<String>(1);
                values.add(this.accept);
                result = values.elements();
            } else {
                result =  super.getHeaders(name);       
            }
            return result;
        }
    }
}

CAAEWS.java

package com.extratechnology.caaews;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.extratechnology.caaews.model.Log;


@Path("et")
@Produces(value = {MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
public class CAAEWS {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Log getJSON() {
        return new Log("JSON!");
    }

    @GET
    @Produces(MediaType.TEXT_XML)
    public Log getXML() {
        return new Log("XML!");
    }

}

Log.java

package com.extratechnology.caaews.model;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Log {
    private String log;

    public Log(String log) {
        this.log = log;
    }

    public String getLog() {
        return log;
    }

    public void setLog(String log) {
        this.log = log;
    }

}

The only thing that intrigued me slightly is that HTTP has two content types for XML.

  • text/xml
  • application/xml

It's configurable in the web.xml, but I'd have to tweak the annotations. Why the two?

--

Footnote:

After writing this, I'm now finding I'm getting HTTP 500 errors. The logs seem to be in some obscure folder when you run the server in Eclipse:

Documents\workspace-sts-3.8.3.RELEASE\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\logs

And I get this written to log:

0:0:0:0:0:0:0:1 - - [25/Nov/2017:16:56:00 +0000] "GET /caaews/webapi/et.xml HTTP/1.1" 500 1082

Does anyone have an idea how to get more sensible log information? Or what I need to do to trap more meaningful stack traces?


It appears the Log class needs a no argument constructor to overcome this. But I concede, the @peeskillet answer is far less cumbersome and uses built in Jersey functionality.


I'm also wondering if javax.servlet.filters don't play well with JAX-RS 2.0 after looking at examples here...


Per other related answers/comments for this question, I ended up implementing an exception handler, so you get more info on the HTTP 500 messages in Jersey..

Here's the code that helps point way to Log.java needing a no argument constructor..

ErrorMessage

package com.extratechnology.caaews.model;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class ErrorMessage {
    private String errorMessage;
    private String errorStackTrace;
    private String cause;
    private String causeStackTrace;
    private int    errorCode;

    public ErrorMessage() {
    }

    public ErrorMessage(
        String errorMessage, 
        String errorStackTrace, 
        String cause,
        String causeStackTrace,
        int errorCode
    ) {
        this.errorMessage = errorMessage;
        this.errorStackTrace = errorStackTrace;
        this.cause = cause;
        this.causeStackTrace = causeStackTrace;
        this.errorCode = errorCode;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    public String getErrorStackTrace() {
        return errorStackTrace;
    }

    public void setErrorStackTrace(String errorStackTrace) {
        this.errorStackTrace = errorStackTrace;
    }

    public String getCause() {
        return cause;
    }

    public void setCause(String cause) {
        this.cause = cause;
    }

    public String getCauseStackTrace() {
        return causeStackTrace;
    }

    public void setCauseStackTrace(String causeStackTrace) {
        this.causeStackTrace = causeStackTrace;
    }

    public int getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }

}

GenericExceptionMapper.java

package com.extratechnology.caaews.exception;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import com.extratechnology.caaews.model.ErrorMessage;

@Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable>{

    @Override
    public Response toResponse(Throwable ex) {
        System.out.println("Stack Trace:");
        ex.printStackTrace();
        System.out.println("Cause:");
        Throwable cause = ex.getCause();
        if (cause != null) {
            cause.printStackTrace();    
        }
        ErrorMessage message = new ErrorMessage(
                ex.getMessage(),
                GenericExceptionMapper.getStackTrack(ex),
                cause.getMessage(),
                GenericExceptionMapper.getStackTrack(cause),
                Status.INTERNAL_SERVER_ERROR.getStatusCode()
                );
        return Response
                .status(Status.INTERNAL_SERVER_ERROR)
                .entity(message)
                .build();
    }

    private static String getStackTrack(Throwable ex) {
        StringBuilder sb = new StringBuilder();
        String ls = System.lineSeparator();
        if (ex != null) {
            StackTraceElement[] steAll = ex.getStackTrace();
            for (StackTraceElement ste : steAll) {
                sb.append(ste.toString());
                sb.append(ls);
            }   
        }
        return sb.toString();
    }
}

The system.out.println gives console messages when debugging and you get a payload back in the web browser too on an error.

eg:

This XML file does not appear to have any style information associated with it. The document tree is shown below.
<errorMessage>
<cause>1 counts of IllegalAnnotationExceptions</cause>
<causeStackTrace>
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(Unknown Source) com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(Unknown Source) com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(Unknown Source) com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(Unknown Source) com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(Unknown Source) com.sun.xml.internal.bind.v2.ContextFactory.createContext(Unknown Source) sun.reflect.GeneratedMethodAccessor20.invoke(Unknown Source) sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) java.lang.reflect.Method.invoke(Unknown Source) javax.xml.bind.ContextFinder.newInstance(Unknown Source) javax.xml.bind.ContextFinder.newInstance(Unknown Source) javax.xml.bind.ContextFinder.find(Unknown Source) javax.xml.bind.JAXBContext.newInstance(Unknown Source) javax.xml.bind.JAXBContext.newInstance(Unknown Source) org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getStoredJaxbContext(AbstractJaxbProvider.java:312) org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getJAXBContext(AbstractJaxbProvider.java:297) org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getMarshaller(AbstractJaxbProvider.java:264) org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getMarshaller(AbstractJaxbProvider.java:231) org.glassfish.jersey.jaxb.internal.AbstractRootElementJaxbProvider.writeTo(AbstractRootElementJaxbProvider.java:175) org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:266) org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:251) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:109) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:85) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1135) org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:662) org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:395) org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:385) org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:280) org.glassfish.jersey.internal.Errors$1.call(Errors.java:272) org.glassfish.jersey.internal.Errors$1.call(Errors.java:268) org.glassfish.jersey.internal.Errors.process(Errors.java:316) org.glassfish.jersey.internal.Errors.process(Errors.java:298) org.glassfish.jersey.internal.Errors.process(Errors.java:268) org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289) org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256) org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703) org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416) org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:217) org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456) java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) java.lang.Thread.run(Unknown Source)
</causeStackTrace>
<errorCode>500</errorCode>
<errorMessage>HTTP 500 Internal Server Error</errorMessage>
<errorStackTrace>
org.glassfish.jersey.jaxb.internal.AbstractRootElementJaxbProvider.writeTo(AbstractRootElementJaxbProvider.java:183) org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:266) org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:251) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:109) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:85) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1135) org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:662) org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:395) org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:385) org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:280) org.glassfish.jersey.internal.Errors$1.call(Errors.java:272) org.glassfish.jersey.internal.Errors$1.call(Errors.java:268) org.glassfish.jersey.internal.Errors.process(Errors.java:316) org.glassfish.jersey.internal.Errors.process(Errors.java:298) org.glassfish.jersey.internal.Errors.process(Errors.java:268) org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289) org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256) org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703) org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416) org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:217) org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456) java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) java.lang.Thread.run(Unknown Source)
</errorStackTrace>
</errorMessage>
JGFMK
  • 8,425
  • 4
  • 58
  • 92
  • 1
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/18059727) – Bill the Lizard Nov 25 '17 at 12:46
  • Well I'm working on adapting that. And I will post answer. So deleting and downvoting is not appropriate. I will post solution here with code when the kinks are ironed out. So I'd appreciate people not deleting answer or downvoting further till then. Thanks! – JGFMK Nov 25 '17 at 13:30
  • 1
    As I explained on the question, you should post an answer when it is ready and not before. Answers are judged for deletion and voting according to the current state. The correct thing to do is to delete this answer and post one when it is complete. – user1803551 Nov 25 '17 at 13:41
  • Well. I look at it from the perspective, there's a solution out there. No need for others to expend energies trying to answer. I'll post back shortly, and it's obvious filters, not @ Path and @ PathParams are the way to go. – JGFMK Nov 25 '17 at 15:08