I am using atmosphere jersey configured using servlet, on the post of my atmosphere Handler I am receiving an abstract object of different types of commands, base on class type I am redirecting to a worker.
I have an abstract class Command and My different commands extends from it
Start
Continue
End
in the post I wanted to route to a different worker
#POST
def incoming(#PathParam("topic") topic: String, cmd: Command) {
cmd.getCmdType match {
case "Start" =>
cmd.asInstanceOf[Start]
//
case "Continue" =>
cmd.asInstanceOf[Continue]
case "End" =>
cmd.asInstanceOf[End]
}
Command.java
#XmlRoot
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.WRAPPER_OBJECT,
property = "type")
#JsonSubTypes({
#Type(value = Start.class, name = "Start"),
#Type(value = Continue.class, name = "Continue"),
#Type(value = End.class, name = "End")})
public abstract class Command
I configured jersey
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.mypackage</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.websocket.messageContentType</param-name>
<param-value>application/json</param-value>
</init-param>
But when I try to push to the socket
var msgJson = {user: userId, eventCode: event, type: "Start"};
var msg = JSON.stringify(msgJson);
subSocket.push(msg);
Complains saying doesn't know how to instantiate the abstract class. So it's ignoring my subtypes annotation
Caused by: javax.xml.bind.UnmarshalException: null
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.handleStreamException(UnmarshallerImpl.java:431) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:368) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:345) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.jersey.json.impl.BaseJSONUnmarshaller.unmarshalJAXBElementFromJSON(BaseJSONUnmarshaller.java:111) ~[jersey-json-1.14.jar:1.14]
at com.sun.jersey.json.impl.BaseJSONUnmarshaller.unmarshalFromJSON(BaseJSONUnmarshaller.java:100) ~[jersey-json-1.14.jar:1.14]
at com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider.readFrom(JSONRootElementProvider.java:129) ~[jersey-json-1.14.jar:1.14]
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.readFrom(AbstractRootElementProvider.java:111) ~[jersey-core-1.14.jar:1.14]
... 41 common frames omitted
Caused by: com.sun.istack.SAXParseException2: Unable to create an instance of mypackage.Command
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:647) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:258) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.createInstance(UnmarshallingContext.java:614) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.StructureLoader.startElement(StructureLoader.java:185) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.XsiTypeLoader.startElement(XsiTypeLoader.java:80) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.ProxyLoader.startElement(ProxyLoader.java:60) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:486) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:464) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:75) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.handleStartElement(StAXStreamConnector.java:247) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:181) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:366) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
... 46 common frames omitted
Caused by: javax.xml.bind.UnmarshalException: Unable to create an instance of mypackage.AbstractCommand
... 58 common frames omitted
Caused by: java.lang.InstantiationException: null
at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48) ~[na:1.7.0_15]
at java.lang.reflect.Constructor.newInstance(Constructor.java:525) ~[na:1.7.0_15]
at com.sun.xml.bind.v2.ClassFactory.create0(ClassFactory.java:122) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.createInstance(ClassBeanInfoImpl.java:269) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.createInstance(UnmarshallingContext.java:608) ~[jaxb-impl-2.2.3-1.jar:2.2.3]
... 55 common frames omitted
Turned out that using JAXB and Jackson was confusing jersey. Shuffled things around and couldn't make it work using #XmlAlsoSee and so on. I found a way to using only Jackson annotations and it works
removed JAXB annotation
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.WRAPPER_OBJECT,
property = "type")
#JsonSubTypes({
#Type(value = Start.class, name = "Start"),
#Type(value = Continue.class, name = "Continue"),
#Type(value = End.class, name = "End")})
public abstract class Command
added extra parameter to servlet in web.xml this tells to use Jackson only and works like a charm
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
Related
I found that the annotation #Cacheable cannot work when the method returns a Java Bean type, this is the complete description:
I annotated #Cacheable on a method to use spring cache:
#Cacheable(cacheNames="userCache", key="#userId")
public User getUser(long userId){
return userRepository.getUserById(userId);
}
And the User class like this:
public class User{
Long userId;
String username;
#JsonSerialize(using = LocalDateTimeSerializer.class)
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime birthDateTime;
}
As you can see, I annotated the relating Jackson annotations to make Jackson deserialization for LocalDateTime types work, and this is the related dependency in pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.5</version>
</dependency>
After that, I call the #Cacheable method getUser like this:
User user = userCache.getUser(1L);
and there throws an exception:
org.redisson.client.RedisException: Unexpected exception while processing command
at org.redisson.command.CommandAsyncService.convertException(CommandAsyncService.java:326)
at org.redisson.command.CommandAsyncService.get(CommandAsyncService.java:123)
at org.redisson.RedissonObject.get(RedissonObject.java:82)
...blabla
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type java.time.LocalDateTime not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 101] (through reference chain: com.stackoverflow.domain.User["birthDateTime"]) at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1764)
at com.fasterxml.jackson.databind.deser.impl.UnsupportedTypeDeserializer.deserialize(UnsupportedTypeDeserializer.java:36)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
3.Before I use the #Cacheable, there is no problem if I get the User from database straightly. But when I begin to use #Cacheable, it always throws the exception above, no matter if I configured those Jackson deserialization for LocalDateTime. Is #Cacheable cannot work well with Java Bean with LocalDateTime property, or just my configuration of Jackson is wrong?
I had the same problem. Spring Cache doesn't use the implicit ObjectMapper used by other Spring components.
Include the module, you already did that.
Create a configuration which will override the default Spring Cache Configuration:
#Configuration
#EnableCaching
public class CacheConfiguration {
#Bean
public RedisSerializationContext.SerializationPair<Object> serializationPair() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule())
.activateDefaultTyping(
objectMapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.EVERYTHING,
JsonTypeInfo.As.PROPERTY
);
return RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));
}
#Bean
public RedisCacheConfiguration redisCacheConfiguration(
#Value("${cache.default-ttl-in-seconds}") Integer ttl,
RedisSerializationContext.SerializationPair<Object> serializationPair
) {
return RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues()
.entryTtl(Duration.ofSeconds(ttl))
.serializeValuesWith(serializationPair);
}
}
I created a rest-api with openapi-codegen. As a parameter there is an object that contains two ENUMs.
e.g.
public enum DocumentType {
FOTO_ID("PHOTO_ID");
[...]
}
If i send a request with a DocumentType other than "PHOTO_ID" I get the following Response
Cannot construct instance of `[...]`, problem: Unexpected value '[...]'
at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 2, column: 21] (through reference chain: [...])
which is correct, but i would like to catch it and send my own ErrorResponse.
I could not find a way to do that.
If somebody has a similar problem, I just found a solution:
You have to set your own ExceptionMapper for JsonMappingException.
It is import to set a Priority, otherwise your Mapper will not be used.
#Named
#Singleton
#Provider
#Priority(1)
public class JsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
#Override
public Response toResponse(JsonMappingException exception) {
[...]
}
}
Credit to:
Jersey unable to catch any Jackson Exception
for pointing out the priority topic.
I am using Spring 4.x in which I am using #Conditional annotation to control the bean registration.
I have classes defined as given below,
#Controller
class SchoolController{
#Autowired
#Qualifier("studentProcessor")
private StudentProcessor studentProcessor;
//Some code
}
#Component("studentProcessor")
class StudentProcessor{
#Autiwired
private SportService sportService;
//Some code
}
#Component
#Conditional(ServiceCondition.class)
class SportService{
//Some code
}
class ServiceCondition implements Condition{
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//Some condition
}
}
When I start the Tomcat, I get this exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'studentProcessor': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.student.service.SportService com.student.processors.StudentProcessor.sportService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.student.service.SportService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Is this the expected behavior?
If not then how do I get rid of this issue?
From your configuration, SportService bean is loaded based on the conditional implementation of ServiceCondition.
So, if the matches method returns false for some reason based on your logic, then SportService is would not be created and will not be available for autowiring.
That being said, StudentProcessor cannot have a concrete #Autowired for SportService.
I am not fully aware of your requirement, but for you to proceed with this configuration, you need to mark autowiring as optional.
#Autiwired
private SportService sportService;
//Some code
to
#Autiwired(required = false)
private SportService sportService;
//Some code
Further, you need to check if the instance is injected or not and then use it.
I would like to create a custom controller in Spring Data Rest with a QueryDSL predicate. I have 3 requirements: a body, headers and query a filter on the result.
It searches resources based on
The URL will be:
POST /geosearch?type=a with body {"geo": {..geojson..}} and with header crs=crs-value.
#RepositoryRestController
#RequestMapping("/geo")
public class GeoController {
...
...
#PostMapping(value = "geosearch", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity customFind(#QuerydslPredicate(root = Domain.class) Predicate predicate,
#RequestBody GeoRequest geoRequest,
#RequestHeader("someheader") String someHeader) throws HttpMediaTypeNotAcceptableException {}
I get the following error:
org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.querydsl.core.types.Predicate]: Specified class is an interface
suppose the controller does not accept query parameters
I'm trying to manually update a bean (it is an argument to a JAX-RS resource method). The value of the field to be set in the bean is to be deserialized from JSON, contextually.
I want to do:
ObjectMapper objectMapper = new ObjectMapper();
// .... <configured, ClassIntrospector obtained for type> ...
BeanProperty prop;
// ... <bean property resolved through ClassIntrospector> ...
AnnotatedMember mutator = prop.getMutator();
JsonFactory jf = new JsonFactory();
JsonParser parser = jf.createParser(textProp);
Object value = objectMapper.getDeserializationContext().readValue(parser, mutator.getRawType());
mutator.setValue(beanInstance, value);
The problem is that Jackson is throwing a NullPointerException:
at com.fasterxml.jackson.databind.DeserializationContext.getTypeFactory(DeserializationContext.java:251)
at com.fasterxml.jackson.databind.DeserializationContext.readValue(DeserializationContext.java:758)
I confirmed with the debugger that the _config field of my DeserializationContext is null, and that this is what is being accessed during my code sequence.
So, what gives? How can I configure this properly so that this works? (Or is there some other way to manually deserialize a JSON fragment to a given type, respecting the JAX-RS resource context / classes?)