Configure Jackson for Google App Engine - java

Is there any way to configure Jackson (ConfiguredObjectMapper) which is used for serializing servlet responses?
#Api(name = "rates",
version = "v1",
title = "Rates API")
public class RatesApi {
static Logger LOG = Logger.getLogger(RatesApi.class.getSimpleName());
#ApiMethod(name = "getLatestRates",
path = "latest",
httpMethod = HttpMethod.GET)
public RatesEnvelope getLatestRates(#Named("base") String base) throws BadRequestException,
InternalServerErrorException {
try {
RatesInfo ratesInfo = DatabaseUtils.getLatestRates(base);
return new RatesEnvelope(ratesInfo.getDate(), base, ratesInfo.getTimestamp(), ratesInfo.getRates());
} catch (IllegalArgumentException e) {
throw new BadRequestException(e.getMessage());
} catch (com.googlecode.objectify.NotFoundException e) {
throw new InternalServerErrorException("no available rates");
}
}
}
My problem is RatesEnvelope class contains BigDecimal fields which should be configured with mapper.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN); to avoid E notation.
web.xml
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<servlet>
<servlet-name>CurrencyWebserviceServlet</servlet-name>
<servlet-class>PACKAGE_NAME.backend.servlet.OpenExchangeRatesWebserviceServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet>
<servlet-name>SystemServiceServlet</servlet-name>
<servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
<init-param>
<param-name>services</param-name>
<param-value>PACKAGE_NAME.backend.spi.RatesApi</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>CurrencyWebserviceServlet</servlet-name>
<url-pattern>/cron/fetchlatest</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>SystemServiceServlet</servlet-name>
<url-pattern>/_ah/spi/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<security-constraint>
<web-resource-collection>
<web-resource-name>all</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<filter>
<filter-name>ObjectifyFilter</filter-name>
<filter-class>com.googlecode.objectify.ObjectifyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ObjectifyFilter</filter-name>
<url-pattern>/*</url-pattern>
<!-- Next three lines are for request dispatcher actions -->
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
</web-app>

Create an instance of Jackson ObjectMapper. Configure as needed by enabling or disabling features you need. Modify your Spring configuration to use it instead of default one. Java configuration would look something like this:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
converter.setObjectMapper(objectMapper);
converters.add(converter);
super.configureMessageConverters(converters);
}

It looks like you're using Cloud Endpoints Frameworks, which doesn't use Jackson annotations. In your case, you'd use an ApiTransformer to achieve what you want. As an example:
#ApiTransformer(RatesEnvelopeTransformer.class)
public class RatesEnvelope {
private BigDecimal someBigDecimalField;
// ...
}
public class RatesEnvelopeTransformer implements Transformer<BigDecimal, String> {
public String transformTo(BigDecimal in) {
return in.toPlainString();
}
public BigDecimal transformFrom(String in) {
return new BigDecimal(in);
}
}

Related

Disable Swagger in Jersey 2.X

I am following the swagger tutorial to swaggerize my web application.
. I am using package scanning and Swagger's BeanConfig for swagger initialization. Is there a way to disable swagger in specific environment (e.g. production)? There are some discussion talking about disabling swagger with SpringMVC
Here is my 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>
io.swagger.jaxrs.listing,
com.expedia.ord.ops.rest
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Hooking up Swagger-Core in your application -->
<servlet>
<servlet-name>SwaggerServlet</servlet-name>
<servlet-class>com.expedia.ord.ops.util.SwaggerServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>SwaggerUI</servlet-name>
<jsp-file>/SwaggerUI/index.html</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>SwaggerUI</servlet-name>
<url-pattern>/api/docs/*</url-pattern>
</servlet-mapping>
Here is my Swagger servlet:
public class SwaggerServlet extends HttpServlet
{
static private final String[] SCHEMES = {"http"};
#Value("${swagger.enable}")
private boolean enableSwagger;
#Value("${swagger.resource.package}")
private String resourcePackage;
#Value("${swagger.host}")
private String host;
#Value("${swagger.basePath}")
private String basePath;
#Value("${swagger.api.version}")
private String version;
#Override
public void init(final ServletConfig config) throws ServletException
{
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, config.getServletContext());
final BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion(version);
beanConfig.setSchemes(SCHEMES);
beanConfig.setHost(host);
beanConfig.setBasePath(basePath);
beanConfig.setResourcePackage(resourcePackage);
beanConfig.setScan(enableSwagger);
}
}

/swagger.json is working but cannot access /api-docs?

I'm trying to use API documentation using Swagger for my 'org.jboss.resteasy' Rest service. After configuration I can access 'http://localhost:8080/myrestswagger/rest/swagger.json' correctly.
it returns following:
{
"swagger": "2.0",
"info": {
"version": "3.0.0",
"title": ""
},
"host": "localhost:8080",
"basePath": "/myrestswagger/rest",
"schemes": [
"http"
]
}
But I cannot access or generate any data on 'http://localhost:8080/myrestswagger/rest/api-docs', please see my classes.
Rest Service Class :
#Path("/countryDetails")
#Api( value = "/countryDetails", description = "countryDetails" )
public class CountryController {
#Path("/countries")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ApiOperation(value = "GetCountries", httpMethod = "GET", notes = "Get Countries against Specific URL", response = Country.class)
public List<Country> getCountries() {
List<Country> listOfCountries = new ArrayList<Country>();
listOfCountries = createCountryList();
return listOfCountries;
}
#Path("/country/{id}")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Country getCountryById(#PathParam("id") int id) {
List<Country> listOfCountries = new ArrayList<Country>();
listOfCountries = createCountryList();
for (Country country : listOfCountries) {
if (country.getId() == id) return country;
}
return null;
}
private List<Country> createCountryList() {
Country indiaCountry = new Country(1, "India");
Country chinaCountry = new Country(4, "China");
Country nepalCountry = new Country(3, "Nepal");
Country bhutanCountry = new Country(2, "Bhutan");
List<Country> listOfCountries = new ArrayList<Country>();
listOfCountries.add(indiaCountry);
listOfCountries.add(chinaCountry);
listOfCountries.add(nepalCountry);
listOfCountries.add(bhutanCountry);
return listOfCountries;
}
}
This is the web.xml
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>io.undertow.servlet.handlers.DefaultServlet</servlet-class>
<init-param>
<param-name>allowed-extensions</param-name>
<param-value>js, css, png, jpg, gif, html, htm, txt, pdf, jpeg, xml, zip, jar</param-value>
</init-param>
<init-param>
<param-name>disallowed-extensions</param-name>
<param-value>class, war</param-value>
</init-param>
</servlet>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/rest</param-value>
</context-param>
<!--While using Spring integration set resteasy.scan to false or don't configure resteasy.scan parameter at all -->
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>resteasy.providers</param-name>
<param-value>
io.swagger.jaxrs.listing.ApiListingResource,
io.swagger.jaxrs.listing.SwaggerSerializers
</param-value>
</context-param>
<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>3.0.0</param-value>
</init-param>
<init-param>
<param-name>swagger.api.basepath</param-name>
<param-value>http://localhost:8080/myrestswagger/rest</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
This is my pom.xml dependancy (maven.project)
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<version>1.5.9</version>
</dependency>
You don't need both swagger.json and api-docs. Both are common names to describe the Swagger definition from your server, which can be used--as a URL--by Swagger UI to render an interactive view of the API.
From looking at the output of your swagger.json, it looks like you're not scanning your resources. Please see about adding your CountryController to the scanning path for the API.

GlassFish Jersey transient field?

I have a small project at my university. I would like to use a REST webserver (on GlassFish) with Jersey.
I tried replace the MOXy to Jackson but I could not do that.
I have a modell class and it is contains few variable. The output is correct JSON or XML. But I want to put Transient annotation to some variable.
The javax.xml.bind.annotation.XmlTransien annotation is not working. I see the variable in the output response.
Here is my modell class:
public class Xyz {
private String a = "value";
private int b = 3;
#XmlTransient
private List<int> list = new LinkedList<>();
// get, set ..
}
And my service class is:
#Path("myresource")
public class MyResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getIt() {
return "Got it!";
}
#GET
#Produces("application/json")
#Path("/json")
public Response getJSON() {
return Response.ok(new Xyz()).build();
}
#GET
#Produces("application/xml")
#Path("/xml")
public Response getXML() {
return Response.ok(new Xyz()).build();
}
}
The web.xml:
<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.example</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.moxy.json.MoxyFeature</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>
Where is the problem? Or how to replace with Jackson? I want to use GlassFish.
Thank you very much!

Why do I get static text/html when returning a 404 Response with Java / Jersey?

I'm working with Java, Jetty and Jersey 2.18 (latest for now) hosted on a Google App Engine.
Let say I have a service such that
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/{userId}")
public Response getUser(#PathParam("userId") String userId)
{
...
}
When I do:
return Response.ok()
.entity(user)
.build();
I correctly receive an application/json content-type and body.
But when I do:
return Response
.status(404)
.entity(new ResponseModel(100, "user not found"))
.build();
same as for returning any 4XX or 5XX status, I receive a text/html content-type along with this HTML body:
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>404 Not Found</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Not Found</h1>
</body>
</html>
and not the object I put in .entity()
Edit: Here is my web.xml
<?xml version="1.0" encoding="utf-8"?>
<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">
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.mypackage.services;org.codehaus.jackson.jaxrs</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
org.glassfish.jersey.server.gae.GaeFeature;
org.glassfish.jersey.server.mvc.jsp.JspMvcFeature;
org.glassfish.jersey.media.multipart.MultiPartFeature;
</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.feature.Trace</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/rest/*</url-pattytern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>home.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>ObjectifyFilter</filter-name>
<filter-class>com.googlecode.objectify.ObjectifyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ObjectifyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring Security Filter -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml </param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>GlobalResponseFilter</filter-name>
<filter-class>com.mypackage.GlobalResponseFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>GlobalResponseFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>everything</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<!-- ** -->
<!-- ** General session timeout in minutes -->
<!-- ** -->
<session-config>
<session-timeout>1440</session-timeout>
</session-config>
</web-app>
ResponseModel is just a basic serializable java class :
import java.io.Serializable;
public class ResponseModel implements Serializable
{
private static final long serialVersionUID = 1L;
private int code;
private Serializable data;
public ResponseModel()
{
}
public ResponseModel(int code, Serializable data)
{
System.err.println("Code " + code + " : " + data);
this.code = code;
this.data = data;
}
public int getCode()
{
return code;
}
public void setCode(int code)
{
this.code = code;
}
public Serializable getData()
{
return data;
}
public void setData(Serializable data)
{
this.data = data;
}
}
Can you try again with the following configuration:
<servlet>
<servlet-name>API</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
...
<init-param>
<param-name>jersey.config.server.response.setStatusOverSendError</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
This flag defines if Jersey - when sending a 4xx or 5xx response status - uses ServletResponse.sendError(flag is false) or ServletResponse.setStatus (flag is true).
Calling ServletResponse.sendError usually resets the response entity and headers and returns a (text/html) error page for the status code.
Since you want to return an own custom error entity, you need to set this flag to true.

Stripes Framework - context returns null

Stripes ActionBeanContext is returning null on all pages where I try to call it.
Here's my shortened ActionBean code:
package stripesbook.action;
public class InitialInfoActionBean implements ActionBean {
private ActionBeanContext context;
public void setContext(ActionBeanContext Context) {
this.context = Context;
}
public ActionBeanContext getContext() {
return this.context;
}
#DefaultHandler
public Resolution nextPage() throws Exception {
return new ForwardResolution("/estimate-info.jsp").addParameter("id", id);
}
}
Here's my web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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_3_0.xsd">
<description>Perceptive Calculator</description>
<display-name>Perceptive Calculator</display-name>
<filter>
<display-name>Stripes Filter</display-name>
<filter-name>StripesFilter</filter-name>
<filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
<init-param>
<param-name>ActionResolver.Packages</param-name>
<param-value>stripesbook.action</param-value>
</init-param>
<init-param>
<param-name>ActionBeanContext.Class</param-name>
<param-value>stripesbook.action.PerceptiveActionBeanContext</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>StripesFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>StripesFilter</filter-name>
<servlet-name>StripesDispatcher</servlet-name>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<servlet>
<servlet-name>StripesDispatcher</servlet-name>
<servlet-class>net.sourceforge.stripes.controller.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>StripesDispatcher</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
I'm not doing anything on my .jsp page except setting the bean with <jsp:useBean id="actionBean" class="stripesbook.action.InitialInfoActionBean" /> and checking for null context with ${ actionBean.context == null }
When posting back to the ActionBean, I'm able to perform various operations but in most cases, setContext never even gets hit.
Figured this out. Instead of
<jsp:useBean id="actionBean" class="stripesbook.action.InitialInfoActionBean" />`
I needed to be using
<stripes:useActionBean beanclass="stripesbook.action.InitialInfoActionBean" executeResolution="false" event="populate" var="actionBean" />
Apparently the regular useBean tag doesn't populate the context.

Categories