Unrecognized field "_links" since the 2.0.0.RC1 of Spring Data REST - spring-data-rest

On one side, I have just update the version of spring-data-rest-webmc to the latest 2.0.0.RC1 version of my server. In this version, the json format change to an HAL format.
On the other side, I have a client which use the spring-hateoas library with the 0.9.0.RELEASE version.
In this client, I use RestTemplate to get a resource from my server like this :
AuthorResource authorResource = restTemplate.getForObject(BASE_URL+"authors/"+ authorId, AuthorResource.class);
The AuthorResource class extends ResourceSupport.
Now, I have this error :
Nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "_links" (class org.example.hateoas.AuthorResource)
How can i configure my client to support this new format ?
I try
#EnableHypermediaSupport(type =
EnableHypermediaSupport.HypermediaType.HAL)
But it does not work.
thx for your help.

Problem is that halMapperObject is not setted because of context is not spring web.
You have to create your own RestTemplate class like this
#Component
public class EraRestTemplate extends RestTemplate implements InitializingBean {
#Autowired
#Qualifier("_halObjectMapper")
ObjectMapper halObjectMapper;
static class HALMessageConverter extends MappingJackson2HttpMessageConverter {
}
#Override
public void afterPropertiesSet() throws Exception {
halObjectMapper.registerModule(new Jackson2HalModule());
HALMessageConverter converter = new HALMessageConverter();
converter.setObjectMapper(halObjectMapper);
this.getMessageConverters().clear();
this.getMessageConverters().add(converter);
}
}
It works fine now for me thanks a friend who knows Spring very well.

Related

Can we access spring bean in Karate feature?

I have a class like below, can I access the myServer object or call handleOperation() method (which can use the injected bean) in Karate Feature file? If yes then may I know how?
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApiApp.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ContextConfiguration(classes = {AcceptanceTestConfiguration.class})
#ActiveProfiles("test")
#KarateOptions(features = "classpath:acceptanceTest/api/myapi.feature", tags = "#myapi")
public class MyAtddTest {
#Autowired
private MyServer myServer;
public void handleOperation() throws Exception {
myServer.handle();
}
}
There is no direct support for spring or the annotations. And not sure if you can mix the test annotations.
But take a look at the Spring MVC Dispatcher example here: https://github.com/intuit/karate/tree/master/karate-mock-servlet#mocking-your-servlet
Specifically how using Java interop you can do anything you want. I recommend getting the spring context using first-principles. For e.g:
ApplicationContext context = new AnnotationConfigApplicationContext(AcceptanceTestConfiguration.class);
And then getting beans out of it. Setting a test profile via System.setProperty() should be simple, search for it. You can do all this in even the karate-config.js and then it should be easy to use from all Scenario-s.
EDIT - also refer: https://github.com/Sdaas/hello-karate

ContainerRequestContext not being injected into business logic

I am using jaxrs 2.0 & am trying to populate an object in the ContainerRequestContext & retrive it in the business logic.
I am populating the object in the ContainerRequestContext like :
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
MyObject myObject = new MyObject();
requestContext.setProperty("myObject", myObject);
}
And I am trying to read it in the business logic like :
#GET
#Path("/custom")
public String data(#Context ContainerRequestContext crc) {
Object pco = crc.getProperty("myObject");
}
I am using this approach from the example shared in : How to inject an object into jersey request context?
However I am getting the ContainerRequestContext crc as null. Can someone tell me what I could be doing wrong ?
I have tested the exact code asked in the question which works as it is with following setup:
spring-boot-starter-parent: 2.3.4.RELEASE
spring-boot-starter: inherited from parent
spring-boot-starter-jersey: inherited from parent
which evaluates to Spring 5.2.9.RELEASE, Jersey 2.30.1
Arquillian with Jersey can work.

redefine static methods with ByteBuddy

Can homebody help me please to give me a hint how to redefine static methods using byte-buddy 1.6.9 ?
I have tried this :
public class Source {
public static String hello(String name) {return null;}
}
public class Target {
public static String hello(String name) {
return "Hello" + name+ "!";
}
}
String helloWorld = new ByteBuddy()
.redefine(Source.class)
.method(named("hello"))
.intercept(MethodDelegation.to(Target.class))
.make()
.load(getClass().getClassLoader())
.getLoaded()
.newInstance()
.hello("World");
I got following Exception :
Exception in thread "main" java.lang.IllegalStateException: Cannot inject already loaded type: class delegation.Source
Thanks
Classes can only be loaded once by each class loader. In order to replace a method, you would need to use a Java agent to hook into the JVM's HotSwap feature.
Byte Buddy provides a class loading strategy that uses such an agent, use:
.load(Source.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());
This does however require you to install a Java agent. On a JDK, you can do so programmatically, by ByteBuddyAgent.install() (included in the byte-buddy-agent artifact). On a JVM, you have to specify the agent on the command line.

Registering a Jackson module for Spring Data REST

I have a working project based on the Spring Data REST example project, and I'm trying to do custom serialization using a Jackson module based on this wiki page.
Here's my Jackson module:
public class CustomModule extends SimpleModule {
public static Logger logger = LoggerFactory.getLogger(CustomModule.class);
public CustomModule() {
super("CustomModule", new Version(1, 0, 0, null));
}
#Override
public void setupModule(SetupContext context) {
logger.debug("CustomModule.setupModule");
SimpleSerializers simpleSerializers = new SimpleSerializers();
simpleSerializers.addSerializer(new CustomDateTimeSerializer());
context.addSerializers(simpleSerializers);
}
}
The wiki page says:
Any Module bean declared within the scope of your ApplicationContext will be picked up by the exporter and registered with its ObjectMapper.
I'm still pretty new to Spring, so I might just be putting my module bean definition in the wrong place; currently it's in src/main/resources/META-INF/spring-data-rest/shared.xml, which is imported from repositories-export.xml:
<bean id="customModule" class="org.hierax.wpa.schema.mapping.CustomModule" />
I don't see the log statement in setupModule, but I do see log output for other classes in the same package.
I'm using Spring Data REST 1.0.0.RC2.
Currently, it's possible to customize a module in Spring Boot like this:
#Bean
public Module customModule() {
return new CustomModule();
}
Reference: Latest Jackson integration improvements in Spring
I've had success using the solution outlined in the wiki entry that you have linked to (although perhaps it has changed since this stack overflow post)
In my instance I was using spring-data-rest-webmvc#1.0.0.RELEASE
Your code seems to be correct and provided that your application context is being loaded correctly I don't see any reason for it not to be working.
I've attached my simpler Module which exemplifies the use of a date formatter:
#Component
public class JsonMarshallingConfigModule extends SimpleModule {
public JsonMarshallingConfigModule() {
super("JsonMarshallingConfigModule", new Version(1, 0, 0, "SNAPSHOT"));
}
#Override public void setupModule(SetupContext context) {
context.getSerializationConfig().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"));
}
}
Perhaps it can be used to outline if it is infact the jackson module that is the problem or spring-data-rest-mvcweb.

GemFire: serialize objects in Java and then deserialize them in c#

To cross the language boundary in Java side the class to be serialized needs to implement the DataSerializable interface; and in order to let the deserializer in c# know what class it is , we need to register a classID. Following the example, I write my class in Java like this:
public class Stuff implements DataSerializable{
static { // note that classID (7) must match C#
Instantiator.register(new Instantiator(Stuff.class,(byte)0x07) {
#Override
public DataSerializable newInstance() {
return new Stuff();
}
});
}
private Stuff(){}
public boolean equals(Object obj) {...}
public int hashCode() {...}
public void toData(DataOutput dataOutput) throws IOException {...}
public void fromData(DataInput dataInput) throws IOException, ClassNotFoundException { ...}
}
It looks OK but when I run it I get this exception:
[warning 2012/03/30 15:06:00.239 JST tid=0x1] Error registering
instantiator on pool:
com.gemstone.gemfire.cache.client.ServerOperationException: : While
performing a remote registerInstantiators at
com.gemstone.gemfire.cache.client.internal.AbstractOp.processAck(AbstractOp.java:247)
at
com.gemstone.gemfire.cache.client.internal.RegisterInstantiatorsOp$RegisterInstantiatorsOpImpl.processResponse(RegisterInstantiatorsOp.java:76)
at
com.gemstone.gemfire.cache.client.internal.AbstractOp.attemptReadResponse(AbstractOp.java:163)
at
com.gemstone.gemfire.cache.client.internal.AbstractOp.attempt(AbstractOp.java:363)
at
com.gemstone.gemfire.cache.client.internal.ConnectionImpl.execute(ConnectionImpl.java:229)
at
com.gemstone.gemfire.cache.client.internal.pooling.PooledConnection.execute(PooledConnection.java:321)
at
com.gemstone.gemfire.cache.client.internal.OpExecutorImpl.executeWithPossibleReAuthentication(OpExecutorImpl.java:646)
at
com.gemstone.gemfire.cache.client.internal.OpExecutorImpl.execute(OpExecutorImpl.java:108)
at
com.gemstone.gemfire.cache.client.internal.PoolImpl.execute(PoolImpl.java:624)
at
com.gemstone.gemfire.cache.client.internal.RegisterInstantiatorsOp.execute(RegisterInstantiatorsOp.java:39)
at
com.gemstone.gemfire.internal.cache.PoolManagerImpl.allPoolsRegisterInstantiator(PoolManagerImpl.java:216)
at
com.gemstone.gemfire.internal.InternalInstantiator.sendRegistrationMessageToServers(InternalInstantiator.java:188)
at
com.gemstone.gemfire.internal.InternalInstantiator._register(InternalInstantiator.java:143)
at
com.gemstone.gemfire.internal.InternalInstantiator.register(InternalInstantiator.java:71)
at com.gemstone.gemfire.Instantiator.register(Instantiator.java:168)
at Stuff.(Stuff.java)
Caused by: java.lang.ClassNotFoundException: Stuff$1
I could not figure out why, is there anyone who has experience can help? Thanks in advance!
In most configurations GemFire servers need to deserialize objects in order to index them, run queries and call listeners. So when you register instantiator the class will be registered on all machines in the Distributed System. Hence, the class itself must be available for loading everywhere in the cluster.
As exception stack trace says the error happens on a remote node.
Check if you have the class Stuff on all machines participating in the cluster. At least on cache servers.