Formatting LocalDateTime for JAX-RS - jax-rs

I'm using resteasy-jaxrs in combination with jackson-datatype-jsr310 to serialize LocalDateTime in the response. This works fine for properties in my classes because I can add the necessary annotation like
#XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
public LocalDateTime getExpirationDate() {
return expirationDate;
}
using the LocalDateTimeAdapter
public class LocalDateTimeAdapter extends XmlAdapter<String, LocalDateTime> {
#Override
public LocalDateTime unmarshal(String s) throws Exception {
return LocalDateTime.parse(s);
}
#Override
public String marshal(LocalDateTime dateTime) throws Exception {
return dateTime.toString();
}
}
In the json I get something like
"expirationDate": "2026-07-17T23:59:59"
But I've a map of objects and this map also can have items of type LocalDateTime and for those there is no annotation and so I get the full object in the json response like
"effectiveDate": {
"dayOfMonth": 1,
"dayOfWeek": "FRIDAY",
"month": "JUNE",
"year": 2018, ...
Is there a way to format every LocalDateTime field, no matter where it comes from?
UPDATE after Paul's answer
Since I already hat the com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency, I added the ObjectMapperContextResolver class and removed the annotation from my LocalDateTime property. Sadly all LocalDateTimes were now serialized as full object again. In the comments of the post I saw that someone added a context-param to the web.xml so that the ObjectMapperContextResolver gets picked up. After adding this to my web.xml, it looks like this.
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Servlet 3.1 Web Application</display-name>
<listener>
<listener-class>com.kopi.web.InitListener</listener-class>
</listener>
<context-param>
<param-name>resteasy.resources</param-name>
<param-value>com.kopi.utils.ObjectMapperContextResolver</param-value>
</context-param>
</web-app>
Now since adding the context-param resteasy.resources, the webapp doesn't start anymore due to
SEVERE: Servlet [com.kopi.rest.RestApplication] in web application [/kopi] threw load() exception
java.lang.RuntimeException: RESTEASY003130: Class is not a root resource. It, or one of its interfaces must be annotated with #Path: com.kopi.utils.ObjectMapperContextResolver implements: javax.ws.rs.ext.ContextResolver
at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:179)
at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:158)
at org.jboss.resteasy.core.ResourceMethodRegistry.addPerRequestResource(ResourceMethodRegistry.java:77)
at org.jboss.resteasy.spi.ResteasyDeployment.registration(ResteasyDeployment.java:482)
at org.jboss.resteasy.spi.ResteasyDeployment.startInternal(ResteasyDeployment.java:279)
at org.jboss.resteasy.spi.ResteasyDeployment.start(ResteasyDeployment.java:86)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.init(ServletContainerDispatcher.java:119)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.init(HttpServletDispatcher.java:36)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1194)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1110)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1000)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4902)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5212)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:724)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:700)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:596)
at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1805)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
In other comments I saw that versions might be an issue. So I'm currently using
org.jboss.resteasy:resteasy-jaxrs 3.5.1.Final
org.jboss.resteasy:resteasy-jackson-provider 3.5.1.Final
org.jboss.resteasy:resteasy-servlet-initializer 3.5.1.Final
com.fasterxml.jackson.datatype:jackson-datatype-jsr310 2.9.5
Thank you, kopi

Instead of the XmlAdapter which you need to declare on individual properties, you could just configure it globally with Jackson using a ContextResolver. This is where you can configure the ObjectMapper and register the JavaTimeModule with the mapper. This configuration will be global so you don't need to use the XmlAdapter. To see how you can configure this, you can see this post.

#XmlJavaTypeAdapter can apply to a whole package (see doc).
When #XmlJavaTypeAdapter annotation is defined at the package level it
applies to all references from within the package to
#XmlJavaTypeAdapter.type().
Try to add the file package-info.java next to the class that has the map. Given the package of the class is com.acme.beans and that of the adapter is com.acme.adapters, the content of the file should be something like:
#XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
package com.acme.beans;
import com.acme.adapters.LocalDateTimeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

Related

GlassFish 4.1.1: Unable to #Inject simple #Stateless in Java EE 7 JAX-RS App

I am using Glassfish 4.1.1 as my Java server. I am trying to #Inject a simple #Stateless bean in my JAX-RS class having #Path annotation. Here is the exception I am getting:
javax.servlet.ServletException: A MultiException has 1 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=MongoCollectionStore,parent=DemoJaxrsApp,qualifiers={},position=-1,optional=false,self=false,unqualified=null,310751270)
Here is my JAX-RS config:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/rest")
public class JaxrsAppConfig extends Application {
}
This is how my JAX-RS resource class looks like:
#Path("/tn-collection")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class DemoJaxrsApp {
#Inject
MongoCollectionStore mongoCollectionStore;
#POST
public List<CollectionTO> getColl() {
return mongoCollectionStore.findAll();
}
}
I am using only 2 dependencies:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
It shouldn't be a problem with dependencies. I am not using any xml files (other than POM.xml and nb-configuration.xml, generated by Netbeans 8.1) as Java EE 7 need not have any config files. I don't know what might have going wrong.
Could anybody please help me out with this UnsatisfiedDependencyException problem?
UPDATE:
Here is my MongoCollectionStore Java class:
#Stateless
public class MongoCollectionStore {
public List<CollectionTO> findAll(MongoConfig mongoConfig) {
List<CollectionTO> tuples = new ArrayList<>();
Gson gson = new Gson();
MongoClient mongoClient = new MongoClient("127.0.0.1", 27017);
MongoDatabase mongoDB = mongoClient.getDatabase("Demo");
MongoCollection<Document> coll = mongoDB.getCollection("DemoCollection");
try(MongoCursor<Document> cursor = coll.find().iterator()) {
while (cursor.hasNext()) {
String jsonDoc = cursor.next().toJson();
CollectionTO tuple = gson.fromJson(jsonDoc, CollectionTO.class);
tuples.add(tuple);
}
}
return tuples;
}
}
I was looking through this problem on internet and found that a CDI bean can only be injected into another CDI bean. They both need to be managed by the container. So, I made my DemoJaxrsApp #RequestScoped, in order to make it a CDI bean.
For guys coming here from Google, Original (and more elaborate) answer can be found here:
Inject an EJB into JAX-RS (RESTful service)
One thing I still don't know is that when I #Injected a #Stateless resource inside my #RequestScoped class, was that resource an EJB? Or, was it a CDI bean? I guess, that's a different question altogether.

Swagger overrides Path-Annotations

I just got swagger to produces a valid swagger.json.
I configured swagger by using the Application-config method.
However, as soon as I override the getClasses-Method to add the swagger resouces, my JAX-RS Path-annotated classes stop working.
The method looks like this
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
resources.add(io.swagger.jaxrs.listing.ApiListingResource.class);
resources.add(io.swagger.jaxrs.listing.SwaggerSerializers.class);
return resources;
}
and invoking super.getClasses() returns am empty set.
I got too many resources in my project, which I would not like to add manually.
Is there any way swagger does not mess with my previous configuration?
Thank you!
You can use a javax.ws.rs.core.Feature. Just register the classes through the callback's FeatureContext. Annotating the feature with #Provider will have it registered through the scanning.
#Provider
public class SwaggerFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(ApiListingResource.class);
context.register(SwaggerSerializers.class);
return true;
}
}
But note that if the application is already registering the resources and providers by class-path scanning, I imagine it should also pick up the Swagger classes, as they are annotated with #Path[1] and #Provider[2]. Those are the annotations the class-path scan looks for.
I haven't tried it myself (I stopped using class-path scanning[3]), but have you tried just not registering them at all? In theory the class-path scan should pick it up.
1. io.swagger.jaxrs.listing.ApiListingResource
2. io.swagger.jaxrs.listing.SwaggerSerializers
3. When to Use JAX-RS Class-path Scanning Mechanism

spring-boot property placeholder

I'm not able to figure out why I am not able to inject values into my application.properties file in spring-boot. external property into the logging.file variable. I have an application.properties file which looks like this
logging.file=${mylogfile}
server.port=${myport}
with corresponding Spring-boot Application class
#PropertySources({
#PropertySource("file:///c:/myfolder/externalprops.properties"),
})
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
and external property file
mylogfile=myoutput.log
myport=8060
When I run my spring-boot 1.0.2.REL application I get the following exception every time I try to inject mylogfile into the logging.file property in application.properties
Exception in thread "main" java.lang.IllegalArgumentException: Could not resolve placeholder 'mylogfile' in string value "${mylogfile}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
Note that I am not having any problems injecting and starting up the application if I inject the server port number on its own.
I am going around in circles on this issue and cannot figure out what I am doing wrong.
I don't think you can use #PropertySource to inject values into "application.properties" - the latter has to be parsed and ready to use before any #Configuration is read, or even known about. Your external properties could go in "${user.dir}/application.properties" and I think that would achieve what you are trying to do.

Spring Data Rest Jpa insert #Lob field

I have a spring data rest service, that expose a resource like:
#Entity
public class Resource{
private String name;
#Lob
private byte[] data;
private String contentType;
}
How should be a json to insert a resource of this type?
AFAIK, SDR does not handle multipart requests or responses yet, as it can only do JSON.
You can run SDR at the same time as a regular Spring MVC servlet (it's one line of code in your config).
I would suggest using a regular Spring MVC controller for your file upload/download, and SDR for the rest (pun intended).
You don't need JSON.
"name" and "contentType" are part of the http header (respectively "Content-Type" and "Content-Disposition: filename")
"data" is the HTTP body. Its encoding depends of "Content-Encoding"
Maybe you should use "ResourceResolvers" plugged with JPA.
Spring Content was designed for exactly this.
Assuming you are using Spring Boot then you can add LOB handling as follows:
pom.xml
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-jpa-boot-starter</artifactId>
<version>0.0.11</version>
</dependency>
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest-boot-starter</artifactId>
<version>0.0.11</version>
</dependency>
Add a Store:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#StoreRestResource(path="resourceContent")
public interface ResourceContentStore extends ContentStore<Resource,String> {}
}
Associate content with your entity entity:
#Entity
public class Resource {
private String name;
#ContentId
private String contentId;
#ContentLength
private long contentLength = 0L;
#MimeType
private String mimeType = "text/plain";
}
That's all that you should need. When you application starts Spring Content will see the dependencies on the Spring Content JPA/REST modules and it will inject an implementation of the ResourceContentStore store for JPA as well as an implementation of a controller (at /resourceContent) that supports that maps GET, POST, PUT and DELETE requests onto the underlying Store interface. The REST endpoint will be available under.
i.e.
curl -X PUT /resourceContent/{resourceId} will create or update an resource's content
curl -X GET /resourceContent/{resourceId} will fetch the resource's content
curl -X DELETE /resourceContent/{resourceId} will delete the resources content
There are a couple of getting started guides here. They use Spring Content for the Filesystem but the modules are interchangeable. The JPA reference guide is here. And there is a tutorial video here.
HTH

Force Glassfish4 to use Jackson instead of Moxy

Glassfish4 is using Moxy to serialize REST responses into JSON. Does anybody know how to configure application to use Jackson instead of Moxy?
You need to register JacksonFeature in your application if you want to use Jackson as your JSON provider (by registering this feature your disable MOXy to be your JSON provider).
You can do it either in Application subclass:
public class MyApplication extends Application {
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<Class<?>>();
// Add root resources.
classes.add(HelloWorldResource.class);
// Add JacksonFeature.
classes.add(JacksonFeature.class);
return classes;
}
}
or in ResourceConfig:
final Application application = new ResourceConfig()
.packages("org.glassfish.jersey.examples.jackson")
.register(MyObjectMapperProvider.class) // No need to register this provider if no special configuration is required.
// Register JacksonFeature.
.register(JacksonFeature.class);
See Jackson section in Jersey Users Guide for more information.
Answer by Michal Gajdos is correct, just to add to that, add this dependency in your pom.xml ,
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.26</version>
</dependency>