Retrofit 2.0 Cant convert Request Body to JSON - post

I hope someone can help me.
I try to send a POST Request with a JSON Body using Retrofit 2.0.
Interface:
public interface Interface {
#POST(/*path*/)
Call<MyResponseObject> sendInt(#Body MyInteger myInt);
}
MyInteger class:
public class MyInteger {
int id;
public MyInteger(int id) {
this.id = id;
}
}
Part of MainActivity:
private Retrofit mRetrofit = null;
private final Interface mService;
...
...
mRetrofit = new Retrofit.Builder()
.baseUrl(/*URL*/)
.addConverterFactory(GsonConverterFactory.create())
.build();
mService = mRetrofit.create(Interface.class);
The call:
MyInteger id = new MyInteger(0);
mService.sendInt(id).enqueue(new Callback<MyResponseObject>() {
#Override
public void onResponse(Call<MyResponseObject> call, Response<MyResponseObject> response) {/*Log something*/}
#Override
public void onFailure(Call<MyResponseObject> call, Throwable t) {}
});
In my Opinion it's like this example:
https://futurestud.io/blog/retrofit-send-objects-in-request-body
But the GsonConverter cant convert MyInteger to JSON..
Here is the Log:
java.lang.IllegalArgumentException: Unable to create #Body converter for class com.??.MyInteger (parameter #1)
for method Interface.sendInt
...
...
Caused by: java.lang.IllegalArgumentException: Could not locate RequestBody converter for class com.??.MyInteger.
Tried:
* retrofit2.BuiltInConverters
* retrofit2.GsonConverterFactory
at retrofit2.Retrofit.nextRequestBodyConverter(Retrofit.java:288)
at retrofit2.Retrofit.requestBodyConverter(Retrofit.java:248)
at retrofit2.RequestFactoryParser.parseParameters(RequestFactoryParser.java:491)

I had the same problem. The root cause was that I was using incompatible libraries.
This combination works for me:
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.0.0-beta4</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.0.0-beta4</version>
</dependency>

Related

Configuring a neo4j test container in Spring Boot with #Configuration file

I'm trying to run spock tests against a neo4j container using the TestContainers library. Previously, I used the test-harness library to run tests against an embedded neo4j database but the need to migrate to using TestContainers has come up. With test-harness I used a configuration file to include some necessary beans for callback functionality. Without these beans, some unit tests are failing.
Previous test-harness method
#ContextConfiguration(classes = [
Neo4jTestHarnessAutoConfiguration,
Neo4jDriverAutoConfiguration,
Neo4jDataAutoConfiguration,
CustomNeo4jConfiguration
])
#ImportAutoConfiguration(classes = [
Neo4jTestHarnessAutoConfiguration,
Neo4jDriverAutoConfiguration,
Neo4jDataAutoConfiguration
])
#Transactional
trait EmbeddedNeo4j {
// Neo4j embedded database is created in Neo4jTestHarnessAutoConfiguration
}
CustomNeo4jConfiguration
#Configuration
#EntityScan(value = {
"data.neo4j.nodes",
"data.neo4j.relationships",
"data.neo4j.queryresults"
})
#EnableNeo4jRepositories("data.neo4j.repository")
#EnableNeo4jAuditing(auditorAwareRef = "auditorAware")
public class CustomNeo4jConfiguration {
#Bean
public AuditorAware<String> auditorAware(){
return new CustomAuditorAwareImpl();
}
#Bean
public BeforeBindCallback neo4jCustomEntitiesCallback(AuditorAware<String> auditorAware) {
return new CustomNeo4jEntitiesCallback(auditorAware);
}
#Bean
public Neo4jConversions neo4jCustomConversions() {
Set<GenericConverter> additionalConverters = Collections.singleton(new InstantStringConverter());
return new Neo4jConversions(additionalConverters);
}
}
The above method works fine. All tests run properly and all beans are created.
TestContainers attempt
#Testcontainers
#Transactional
trait EmbeddedNeo4j {
#Shared
static final Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:4.3.9")
.withReuse(true)
#DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
neo4jContainer.start()
registry.add("spring.neo4j.uri", neo4jContainer::getBoltUrl)
registry.add("spring.neo4j.authentication.username", () -> "neo4j")
registry.add("spring.neo4j.authentication.password", neo4jContainer::getAdminPassword)
}
}
With the above, all tests successfully run against the test container. However, any test that needs the functionality of the beans in the CustomNeo4jConfiguration fails.
What I've tried
I've attempted to use the same #ContextConfiguration annotations in various configurations, such as...
#ContextConfiguration(classes = [
Neo4jDriverAutoConfiguration,
Neo4jDataAutoConfiguration,
CustomNeo4jConfiguration
])
#ImportAutoConfiguration(classes = [
Neo4jDriverAutoConfiguration,
Neo4jDataAutoConfiguration
])
#Transactional
trait EmbeddedNeo4j {
TestContainer code here...
However, this fails with the following stack trace:
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248)
at org.spockframework.spring.SpringTestContextManager.prepareTestInstance(SpringTestContextManager.java:56)
at org.spockframework.spring.SpringInterceptor.interceptInitializerMethod(SpringInterceptor.java:43)
at org.spockframework.runtime.extension.AbstractMethodInterceptor.intercept(AbstractMethodInterceptor.java:24)
at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:101)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.spockframework.runtime.model.MethodInfo.invoke(MethodInfo.java:148)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:133)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'auditFieldRepository' defined in data.neo4j.repository.AuditFieldRepository defined in #EnableNeo4jRepositories declared on CustomNeo4jConfiguration: Cannot resolve reference to bean 'neo4jTemplate' while setting bean property 'neo4jOperations'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'neo4jTemplate' available
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1707)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1452)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:936)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:132)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
... 64 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'neo4jTemplate' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:874)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1344)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:309)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
... 82 more
I've also attempted in various ways to create the missing beans, but that doesn't feel like the right path to take and that didn't produce any results.
What's the right way to use a #Configuration file with TestContainers?
Appreciate any help!
If I interpret your mixture of Java and Scala code correctly, I would recommend the following approach, but only if you insist on not using #SpringBootTest.
Also: You are using the outdated neo4j-java-driver-spring-boot-starter classes, they aren't needed for a while now in recent Spring Boot and also will potentially break SDN6+.
Please note: You should never import auto configuration classes and use them as ContextConfiguration at the same time. They are only meant to be imported via #ImportAutoConfiguration, but even that is redundant or overly complicated most of the time.
First of all, here's the pom so you get the proper dependencies:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>neowithapoc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>neowithapoc</name>
<description>neowithapoc</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.17.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>neo4j</artifactId>
<version>1.17.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Solution 1
Here's a solution that goes into what you have there, with a trait - or in case of pure java - abstract test class:
package com.example.neowithapoc;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration;
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.containers.Neo4jLabsPlugin;
#TestConfiguration
#ImportAutoConfiguration({
Neo4jAutoConfiguration.class,
Neo4jDataAutoConfiguration.class
})
#EnableTransactionManagement
#EnableNeo4jRepositories(considerNestedRepositories = true)
public abstract class EmbeddedNeo4jConfig {
static final Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:4.4")
.withLabsPlugins(Neo4jLabsPlugin.APOC)
.withReuse(true);
#DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
neo4j.start();
registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
registry.add("spring.neo4j.authentication.username", () -> "neo4j");
registry.add("spring.neo4j.authentication.password", neo4j::getAdminPassword);
}
}
Use like this:
package com.example.neowithapoc;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.data.neo4j.core.Neo4jTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = EmbeddedNeo4jConfig.class)
public class SomeTest extends EmbeddedNeo4jConfig {
#Test
void whatever(#Autowired Neo4jTemplate template, #Autowired Neo4jClient client) {
assertThat(template).isNotNull();
assertThat(client).isNotNull();
String apoc = client
.query("RETURN apoc.version() AS output")
.fetchAs(String.class)
.first().get();
assertThat(apoc).startsWith("4.4");
}
}
Solution 2
I do prefer this way simpler and less complicated approach:
package com.example.neowithapoc;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.data.neo4j.core.Neo4jTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.annotation.Transactional;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.containers.Neo4jLabsPlugin;
import org.testcontainers.junit.jupiter.Testcontainers;
#SpringBootTest
#Transactional
#Testcontainers(disabledWithoutDocker = true)
public class SomeSpringBootTest {
static final Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:4.4")
.withLabsPlugins(Neo4jLabsPlugin.APOC)
.withReuse(true);
#DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
neo4j.start();
registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
registry.add("spring.neo4j.authentication.username", () -> "neo4j");
registry.add("spring.neo4j.authentication.password", neo4j::getAdminPassword);
}
#Test
void whatever(#Autowired Neo4jTemplate template, #Autowired Neo4jClient client) {
assertThat(template).isNotNull();
assertThat(client).isNotNull();
String apoc = client
.query("RETURN apoc.version() AS output")
.fetchAs(String.class)
.first().get();
assertThat(apoc).startsWith("4.4");
}
}
Bonus questions custom conversions
Given the following class:
package com.example.neowithapoc;
public class SomeObject {
private final String value;
public SomeObject(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
It is not an entity and it needs some special treatment during conversions.
It can be converted back and forth like this:
import java.util.Set;
import org.neo4j.driver.Value;
import org.neo4j.driver.Values;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
public class SomeObjectConverter implements GenericConverter {
#Override public Set<ConvertiblePair> getConvertibleTypes() {
return Set.of(
new ConvertiblePair(Value.class, SomeObject.class),
new ConvertiblePair(SomeObject.class, Value.class)
);
}
#Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return Value.class.isAssignableFrom(targetType.getType()) ? Values.NULL : null;
} else if (source instanceof SomeObject someObject) {
return Values.value(someObject.getValue());
} else if (source instanceof Value value) {
return new SomeObject(value.asString());
} else
throw new IllegalArgumentException();
}
}
Configure it with a #TestConfiguration (or on your main config) like this
#TestConfiguration
static class AdditionalBeans {
#Bean
SomeObjectConverter someObjectConverter() {
return new SomeObjectConverter();
}
#Bean
Neo4jConversions neo4jConversions(SomeObjectConverter someObjectConverter) {
return new Neo4jConversions(List.of(someObjectConverter));
}
}
In both cases this class is a static nested class of either EmbeddedNeo4jConfig and SomeSpringBootTest.
In the former you normally would just declare the bean method directly, but as I tried to recreate your "trait" as much as possible in Java and I do extend that class, this is not possible. Tests must not have their own #Bean methods.
After that, test like this
#Node
static class ContainerNode {
#Id #GeneratedValue
Long id;
SomeObject someObject;
public Long getId() {
return id;
}
public SomeObject getSomeObject() {
return someObject;
}
}
#Test
void conversionsShouldWork(#Autowired Neo4jTemplate template) {
var container = template.find(ContainerNode.class)
.matching("CREATE (n:ContainerNode {someObject: 'Hello'}) RETURN n")
.one();
assertThat(container).map(ContainerNode::getSomeObject).map(SomeObject::getValue).hasValue("Hello");
}
will work just fine, also with the repository.
This test here will fail
#Test
void conversionsShouldWork(#Autowired Neo4jClient client) {
var anObject = client.query("RETURN 'whatever'").fetchAs(SomeObject.class).first();
assertThat(anObject).map(SomeObject::getValue).hasValue("whatever");
}
but the Spring Data Neo4j fixed this in 6.3.3 https://github.com/spring-projects/spring-data-neo4j/issues/2594 and you would be able to manually add it to the client like this:
#TestConfiguration
static class AdditionalBeans {
#Bean
SomeObjectConverter someObjectConverter() {
return new SomeObjectConverter();
}
#Bean
Neo4jConversions neo4jConversions(SomeObjectConverter someObjectConverter) {
return new Neo4jConversions(List.of(someObjectConverter));
}
#Bean
public Neo4jClient neo4jClient(
Driver driver,
DatabaseSelectionProvider databaseSelectionProvider,
Neo4jConversions neo4jConversions) {
return Neo4jClient.with(driver)
.withDatabaseSelectionProvider(databaseSelectionProvider)
.withNeo4jConversions(neo4jConversions)
.build();
}
}
Than this test will work just fine:
#Test
void clientConversionsShouldWork(#Autowired Neo4jClient client) {
var anObject = client.query("RETURN 'whatever'").fetchAs(SomeObject.class).first();
assertThat(anObject).map(SomeObject::getValue).hasValue("whatever");
}

QuickFIX/J: Type 'quickfix/field/HaltReason' is not assignable to 'quickfix/IntField'

Summary
We have a quickfix client which receives SecurityDefiniton and SecurityDefinitionUpdateReport messages. These are bulk datas. When we logged to the server they send messages around 8000. At the end they're sending SecurityStatus message. In this part we are getting an exception.
Fix protocol version: FIX50SP2 with FIXT1.1
Quickfix message dependency
<dependency>
<groupId>org.quickfixj</groupId>
<artifactId>quickfixj-messages-fix50sp2</artifactId>
<version>2.3.0</version>
</dependency>
Quickfix Core
<dependency>
<groupId>org.quickfixj</groupId>
<artifactId>quickfixj-core</artifactId>
<version>2.0.0</version>
</dependency>
Exception we received;
Exception in thread "pool-5-thread-1" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
quickfix/fix50sp2/SecurityStatus.get(Lquickfix/field/HaltReason;)Lquickfix/field/HaltReason; #2: invokevirtual
Reason:
Type 'quickfix/field/HaltReason' (current frame, stack[1]) is not assignable to 'quickfix/IntField'
Current Frame:
bci: #2
flags: { }
locals: { 'quickfix/fix50sp2/SecurityStatus', 'quickfix/field/HaltReason' }
stack: { 'quickfix/fix50sp2/SecurityStatus', 'quickfix/field/HaltReason' }
Bytecode:
0x0000000: 2a2b b600 1557 2bb0
at quickfix.fix50sp2.MessageFactory.create(MessageFactory.java:297)
at foo.bar.data.plugin.fix.api.MessageFactory.MessageFactorySp2.create(MessageFactorySp2.java:93)
at quickfix.MessageUtils.parse(MessageUtils.java:145)
at quickfix.mina.AbstractIoHandler.messageReceived(AbstractIoHandler.java:131)
at org.apache.mina.core.filterchain.DefaultIoFilterChain$TailFilter.messageReceived(DefaultIoFilterChain.java:858)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:542)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$1300(DefaultIoFilterChain.java:48)
at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.messageReceived(DefaultIoFilterChain.java:947)
at org.apache.mina.filter.codec.ProtocolCodecFilter$ProtocolDecoderOutputImpl.flush(ProtocolCodecFilter.java:398)
at org.apache.mina.filter.codec.ProtocolCodecFilter.messageReceived(ProtocolCodecFilter.java:234)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:542)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$1300(DefaultIoFilterChain.java:48)
at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.messageReceived(DefaultIoFilterChain.java:947)
at org.apache.mina.core.filterchain.IoFilterAdapter.messageReceived(IoFilterAdapter.java:109)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:542)
at org.apache.mina.core.filterchain.DefaultIoFilterChain.fireMessageReceived(DefaultIoFilterChain.java:535)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.read(AbstractPollingIoProcessor.java:703)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:659)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:648)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.access$600(AbstractPollingIoProcessor.java:68)
at org.apache.mina.core.polling.AbstractPollingIoProcessor$Processor.run(AbstractPollingIoProcessor.java:1120)
at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:64)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
We couldn't get SecurityStatus fix message. The problem we thought was about datadictionary. As a field HaltReason is a CharField
public class HaltReason extends CharField {
static final long serialVersionUID = 20050617L;
public static final int FIELD = 327;
public static final char NEWS_DISSEMINATION = 'D';
public static final char ORDER_INFLUX = 'E';
public static final char ORDER_IMBALANCE = 'I';
public static final char ADDITIONAL_INFORMATION = 'M';
public static final char NEWS_PENDING = 'P';
public static final char EQUIPMENT_CHANGEOVER = 'X';
When we look FIX50SP2 default datadictionary halt reason field is INT.
<field number="327" name="HaltReasonInt" type="INT">
<value enum="0" description="NEWS_DISSEMINATION"/>
<value enum="1" description="ORDER_INFLUX"/>
<value enum="2" description="ORDER_IMBALANCE"/>
<value enum="3" description="ADDITIONAL_INFORMATION"/>
<value enum="4" description="NEWS_PENDING"/>
<value enum="5" description="EQUIPMENT_CHANGEOVER"/>
</field>
We tried convert dictionary field to HaltReasonChar and the type CHAR but it didn't work. Did you ever get an exception like this?
Here's the security status message which I received.
8=FIXT.1.19=00017835=f49=BI_TEST56=LIABR34=589857=TRTK152=20220208-20:07:15.9281180=R1181=28513331350=285133255=T2-ON48=3762690422=M336=148325=N60=20220208-20:07:15.92810=245
It doesn't look like the HaltReason field (tag 327) is included in your status message. Is it an optional or required field for the SecurityStatus message type? You can control that in your data dictionary definition.
The dictionary for FIX50SP2.xml I have indicates this field is not required, but yours could be different.
Be careful of broker-supplied dictionaries; they are frequently wrong or outdated.
The following worked fine for me: report is of type quickfix.fix44.SecurityStatus
try {
response.setHaltReason(report.getString(327));
} catch (Exception e) {
log.error...
}

How to override the default encrypted property prefix/suffix?

Jasypt expects encrypted properties to be wrapped with "ENC(...)".
I'm looking for a way to override the default jasypt encrypted property prefix and suffix with a custom ones like "secure[...]".
Is there a way to achieve this?
If you use pure jasypt library it is required to write your own version of existing EncryptableProperties as default one supports only ENC( as prefix and ) as suffix.
public class EncryptableProperties extends Properties {
private final String prefix;
private final String suffix;
private final StringEncryptor encryptor;
public EncryptableProperties(final Properties defaults, final StringEncryptor encryptor,
final String prefix, final String suffix) {
super(defaults);
this.encryptor = encryptor;
this.prefix = prefix;
this.suffix = suffix;
}
public EncryptableProperties(final Properties defaults, final StringEncryptor encryptor){
this(defaults,encryptor,"ENC(", ")");
}
#Override
public String getProperty(String key) {
String value = super.getProperty(key);
return decode(value);
}
private String decode(String value) {
if (value == null) {
return value;
}
String decryptedValue = value;
if (value.startsWith(prefix) && value.endsWith(suffix)) {
int start = prefix.length();
int end = value.length() - suffix.length();
String encryptedValue = value.substring(start, end);
decryptedValue = encryptor.decrypt(encryptedValue);
}
return decryptedValue;
}
}
Usage:
Properties encryptableProperties = new EncryptableProperties(properties,stringEncryptor,"CUSTOM_PREFIX(",")");
If you use spring boot integration
You could do this by using following two properties
jasypt.encryptor.property.prefix
jasypt.encryptor.property.suffix
Default one is:
jasypt.encryptor.property.prefix=ENC(
jasypt.encryptor.property.suffix=)
These properties are available with following library
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot</artifactId>
<version>2.0.0</version>
</dependency>

NullPointerException at doing Session.load() while first time trying out Neo4J OGM

I am trying out Neo4J OGM for first time from simple example given on its github page and with the help of its manual.
I am running neo4j-community-3.0.0-M05.
I am getting following exception:
Exception in thread "main" java.lang.NullPointerException
at org.neo4j.ogm.session.delegates.LoadOneDelegate.lookup(LoadOneDelegate.java:56)
at org.neo4j.ogm.session.delegates.LoadOneDelegate.load(LoadOneDelegate.java:49)
at org.neo4j.ogm.session.delegates.LoadOneDelegate.load(LoadOneDelegate.java:39)
at org.neo4j.ogm.session.Neo4jSession.load(Neo4jSession.java:137)
at org.neo4j.ogm.session.Capability$LoadOne$load.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)
at Main.main(Main.groovy:24)
The exception occurs on line 24 of Main.groovy. I debugged. It occurs on session.load() call.
I feel that this must be beacuse I must have made some mistake in setting up dependencies. But cant figure it out.
This is my code:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mahesha999.exp</groupId>
<artifactId>Neo4JTemp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-core</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-http-driver</artifactId>
<version>2.0.4</version>
</dependency>
</dependencies>
</project>
ogm.properties
driver=org.neo4j.ogm.drivers.http.driver.HttpDriver
URI=http://neo4j:password#localhost:7474
Actor.groovy
import org.neo4j.ogm.annotation.NodeEntity
import org.neo4j.ogm.annotation.Relationship
import org.neo4j.ogm.annotation.GraphId
#NodeEntity
public class Actor {
#GraphId
private Long id;
private String name;
#Relationship(type = "ACTS_IN", direction = "OUTGOING")
private Set<Movie> movies = new HashSet<>();
public Actor() { }
public Actor(String name) {
this.name = name;
}
public void actsIn(Movie movie) {
movies<< movie;
movie.getActors() << this;
}
}
Movie.groovy
import org.neo4j.ogm.annotation.NodeEntity
import org.neo4j.ogm.annotation.Relationship
import org.neo4j.ogm.annotation.GraphId
#NodeEntity
public class Movie {
#GraphId
private Long id;
private String title;
private int released;
#Relationship(type = "ACTS_IN", direction = "INCOMING")
List<Actor> actors = [];
public Movie() {}
public Movie(String title, int year) {
this.title = title;
this.released = year;
}
}
Main.groovy
1 import org.neo4j.ogm.session.Session
2 import org.neo4j.ogm.session.SessionFactory
3
4 class Main {
5
6 static main(def args)
7 {
8 //Set up the Session
9 SessionFactory sessionFactory = new SessionFactory("movies.domain");
10 Session session = sessionFactory.openSession();
11
12 Movie movie = new Movie("The Matrix", 1999);
13
14 Actor keanu = new Actor("Keanu Reeves");
15 keanu.actsIn(movie);
16
17 Actor carrie = new Actor("Carrie-Ann Moss");
18 carrie.actsIn(movie);
19
20 //Persist the movie. This persists the actors as well.
21 session.save(movie);
22
23 //Load a movie
24 Movie matrix = session.load(Movie.class, movie.id);
25 for(Actor actor : matrix.getActors()) {
26 System.out.println("Actor: " + actor.name);
27 }
28 }
29 }
The package being scanned (movies.domain) does not seem to contain your entity classes. That's why the entity is not saved, and the id is null.
The OGM reference guide (with tutorial) is here: http://neo4j.com/docs/ogm-manual/current/
And a small sample app: https://github.com/neo4j-examples/neo4j-ogm-university/tree/2.0

SnakeYaml class not found exception

When I parse config.yaml using SnakeYaml 1.14 I get a "Class not found exception". The following is the code used for parsing. I have used maven to build the project.
public class AppConfigurationReader
{
private static final String CONFIG_FILE = "config.yaml";
private static String fileContents = null;
private static final Logger logger = LoggerFactory.getLogger(AppConfigurationReader.class);
public static synchronized AppConfiguration getConfiguration() {
return getConfiguration(false);
}
public static synchronized AppConfiguration getConfiguration(Boolean forceReload) {
try {
Yaml yaml = new Yaml();
if(null == fileContents || forceReload) {
fileContents = read(CONFIG_FILE);
}
yaml.loadAs(fileContents, AppConfiguration.class);
return yaml.loadAs(fileContents, AppConfiguration.class);
}
catch (Exception ex) {
ex.printStackTrace();
logger.error("Error loading fileContents {}", ex.getStackTrace()[0]);
return null;
}
}
private static String read(String filename) {
try {
return new Scanner(new File(filename)).useDelimiter("\\A").next();
} catch (Exception ex) {
logger.error("Error scanning configuration file {}", filename);
return null;
}
}
}
I too had this, it was due to an incorrect set of dependencies.
I had used
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
when I should have used
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
The difference being that the latter includes org.yaml:snakeyaml:jar:1.27:compile
May be I am bit late to respond, but it will help others in future.
This issue comes when your class is not able to load the class, sometimes even if it is present in your classpath also.
I encountered this Issue and can be handled in this way.
package my.test.project;
import java.io.InputStream;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor;
public class MyTestClass {
public static void main(String[] args) {
InputStream input = MyTestClass.class.getClassLoader().getResourceAsStream("test.yml");
Yaml y = new Yaml(new CustomClassLoaderConstructor(MyTestClass.class.getClassLoader()));
TestConfig test =y.loadAs(input, TestConfig.class);
System.out.println(test);
}
}
You need to initialize Yaml object with CustomClassLoaderConstructor it will help load the bean class before it was actually used internally.
I have found a similar error but dumping a file.
You could write the complete name of the class in yaml.load instruction.
For example, if AppConfiguration.class was in org.example.package1, you would write something like:
yaml.loadAs(fileContents, org.example.package1.AppConfiguration.class);
Seems like snakeyaml libraries are not included in your jar file, you have to use the maven assembly plugin eather than just package, so that all the dependency jars gets included

Resources