Jackson Kotlin plugin not used by Spring Boot - spring

I can't force Spring to use Kotlin module for Jackson.
The problem is that Jackson can't parse JSON into data class.
//Exception
2018-02-23 13:29:09.046 ERROR 24730 --- [nio-9300-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/services] threw exception [Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [*.model.User]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Parameter specified as non-null is null: method *.model.User.<init>, parameter name] with root cause
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method *.User.<init>, parameter name
//JSON
{
"name": "name",
"surname": "surname",
"email": "email",
"password": "pswd"
}
//Model
#Entity
#Table
data class User(
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
var userId: Long?,
var name: String,
var surname: String,
var email: String,
var password: String,
(...)
): Resource() {
(...)
}
I tried to config Jackson, but it didn't help much. What's strange, inside #Bean method where I configure ObjectMapper, everything works fine.
Also, when I added default values for non-nullable fields, they weren't overwritten.
#Configuration
class JacksonConfig {
#Bean
fun mappingJackson2HttpMessageConverter(): MappingJackson2HttpMessageConverter {
val mapper = ObjectMapper().registerKotlinModule()
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
var user = mapper.readValue<User>("{\n" +
"\t\"name\": \"name\",\n" +
"\t\"surname\": \"surname\",\n" +
"\t\"email\": \"email\",\n" +
"\t\"password\": \"pswd\"\n" +
"}")
return MappingJackson2HttpMessageConverter(mapper)
}
}
What might be important is that model is in the different project than the application itself.
Kotlin version is 1.20
Jackson dependencies:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version> <!--2.9.4-->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>${jackson.version}</version>
</dependency>
And what I tried to this moment:
All of these answers
Similiar problem but probably different case anyway
And some other, not worth noticing.

I had structure like:
abstract class AbstractController (...) {
fun save(#RequestMapping entity: T) {
(...)
}
And to enable it I used
class DeriveredController (...) {
#PostMapping
override fun save(entity: Derivered) {
(...)
}
}
Problem was with lack of #RequestMapping in derivered function.

Related

Start Springboot without any Undertow Configuration

I have a project which mixes 2 dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-installed-adapter</artifactId>
<version>11.0.2</version>
</dependency>
I'm getting a transitiv dependency for Undertow, which comes from
keycloak-installed-adapter
As far as I understood, Undertow is used internally within the KeyCloak libraries.
I don't want my Springboot Application to start with Undertow, nor I want that anything related with Undertow gets configured at Springboot level.
I can't find to completely exclude any Undertow configuration for Springboot. Does anyone have an idea?
Update
I now have the following:
#SpringBootApplication
#EnableAutoConfiguration(exclude = {EmbeddedUndertow.class})
public class SpringbootTest {
public static void main(String[] args) {
SpringApplication.run(SpringbootTest.class, args);
}
}
But i'm getting following exception when starting my service:
org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'undertowEmbeddedServletContainerFactory' defined in class path resource [org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration$EmbeddedUndertow.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory]: Factory method 'undertowEmbeddedServletContainerFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: io/undertow/servlet/api/SessionPersistenceManager
Knowing that the EmbeddedUndertow Class lokks like:
#Configuration
#ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
#ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedUndertow {
#Bean
public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
return new UndertowEmbeddedServletContainerFactory();
}
}
How would you ignore this calss from the scanning?

Reactive GraphQL

Trying to implement reactive graphql and running into some issues.
pom dependencies,
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-kickstart-spring-boot-starter-webflux</artifactId>
<version>7.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.graphql-java-kickstart/graphql-kickstart-spring-boot-starter-tools -->
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-kickstart-spring-boot-starter-tools</artifactId>
<version>7.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.graphql-java-kickstart/graphiql-spring-boot-starter -->
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>7.0.1</version>
</dependency>
Schema:-
type Query {
store(storeId: Int!): Store!
stores: [Store!]!
}
type Store {
storeId: Int!
name: String!
address: String!
city: String!
zip: String!
}
Resolver:-
#Component
public class StoreQuery implements GraphQLQueryResolver {
#Autowired
private final StoreDao dao;
public Store store(int storeId) {
return dao.findById(storeId);
}
public List<Store> stores() {
return dao.findAll();
}
}
Here, If I return Flux or Mono, getting the following error.
"class": "org.springframework.beans.factory.BeanCreationException",
"msg": "Error creating bean with name 'schemaParser' defined in class path resource [graphql/kickstart/tools/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [graphql.kickstart.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is java.lang.ClassCastException: class graphql.kickstart.tools.ParameterizedTypeImpl cannot be cast to class java.lang.Class (graphql.kickstart.tools.ParameterizedTypeImpl is in unnamed module of loader 'app'; java.lang.Class is in module java.base of loader 'bootstrap')",
"class": "java.lang.ClassCastException",
"msg": "class graphql.kickstart.tools.ParameterizedTypeImpl cannot be cast to class java.lang.Class (graphql.kickstart.tools.ParameterizedTypeImpl is in unnamed module of loader 'app'; java.lang.Class is in module java.base of loader 'bootstrap')",
So, trying to return as List.
But converting Flux to List using blocking calls also throwing error,
public List<Store> findAll() {
return Flux.from(storeCollection.find()).collectList().block();
}
Here, getting the error below.
Exception while fetching data (/stores) : block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-kqueue-2
Using graphiql for testing.
http://localhost:8089/graphiql
As the error suggests, you can't use blocking operation in non-blocking webflux.
public Flux<Store> findAll() {
return Flux.fromIterable(storeCollection.find());
or
return Flux.fromStream(storeCollection.find());
}
You can't use blocking operation in non-blocking webflux. If you use kickstart v.7.0.1 you should return CompletableFuture
public CompletableFuture<List<Store>> findAll() {
return Flux.fromIterable(dao.findAll())
.collectList()
.toFuture();
}

Spring Boot WebClient XML

My spring boot application wants to use Webclient to make an http request (XML request body) and receives XML response. Hence I created another spring boot application with jackson-dataformat-xml and created an endpoint to receive and return XML as below.
spring-boot-version=2.2.5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
#PostMapping(value = "/api",
consumes = MediaType.APPLICATION_XML_VALUE,
produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<MyXmlResponse> trip(#RequestBody MyXmlRequest request) throws Exception {
MyXmlResponse response = new MyXmlResponse();
response.setStatus("SUCCESS");
response.setTripID(request.getTripID());
return ResponseEntity.ok().body(response);
}
It works perfect and obviously no JaxB annotations are required as I use jackson-dataformat-xml. Also the request XML can be case-insensitive.
Now, in my first application I want to consume this API via webclient. I read that Spring webflux do not support Jackson-dataformat-xml yet. Hence I have to annotate my classes with Jaxb annotations.
spring-boot-version=2.2.5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
webClient.post()
.uri(URI.create("url-to-api-endpoint"))
.body(Mono.just(myXmlRequest), MyXmlRequest.class)
.exchange()
.doOnSuccess(response -> {
HttpStatus statusCode = response.statusCode();
log.info("Status code of external system request {}", statusCode);
})
.doOnError(onError -> {
log.error("Error on connecting to external system {}", onError.getMessage());
})
.flatMap(response -> response.bodyToMono(MyXmlResponse.class))
.subscribe(this::handleResponse);
Above code throws an exception as follows
org.springframework.webreactive.function.UnsupportedMediaTypeException: Content type 'application/xml' not supported for bodyType=com.example.MyXmlRequest
at org.springframework.web.reactive.function.BodyInserters.unsupportedError(BodyInserters.java:391)
I fixed this problem by annotating with XmlRootElement as follows
#Getter #Setter #NoArgsConstructor #ToString
#XmlRootElement()
public class MyXmlRequest {
private String attribute1;
}
On the next attempt I got another error as follows
reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/xml' not supported for bodyType=com.example.MyXmlResponse
Caused by: org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/xml' not supported for bodyType=com.example.MyXmlResponse
This could be solved by annotating MyXmlResponse with XmlRootElement as follows
#Getter #Setter #NoArgsConstructor #ToString
#XmlRootElement()
public class MyXmlResponse {
private String attr1;
private String attr2;
}
This time I get unmarshallexception as follows
reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.core.codec.DecodingException: Could not unmarshal XML to class com.example.MyXmlResponse; nested exception is javax.xml.bind.UnmarshalException
- with linked exception:
[com.sun.istack.internal.SAXParseException2; lineNumber: 1; columnNumber: 15; unexpected element (uri:"", local:"MyXmlResponse"). Expected elements are <{}myXmlResponse>]
Caused by: org.springframework.core.codec.DecodingException: Could not unmarshal XML to class com.example.MyXmlResponse; nested exception is javax.xml.bind.UnmarshalException
- with linked exception:
I fixed it with additional attributes passed to annotation as follows.
#XmlRootElement(name = "MyXmlResponse", namespace = "")
public class MyXmlResponse {
In future, my XML structures going to be tremendously complex. I want to know if I am doing it the right way.

How to access a in-memory database in spring

I am developing a standalone java application using in-memory embedded database. I refereed few documents and I wrote the following code. I am using spring Boot.
These are the steps I did so far:
In, pom file I added these dependencies.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.36</version>
</dependency>
In application.properties file
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=admin
I created a scripts schema.sql and data.sql
schema.sql
CREATE TABLE users
(
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
email varchar(100) DEFAULT NULL,
PRIMARY KEY (id)
);
data.sql
insert into users(id, name, email) values(1,'demouser','user#test.com');
Here is my repository class.
#Repository
public class UserRepository {
#Autowired
private JdbcTemplate jdbcTemplate;
#Transactional(readOnly = true)
public List<User> findAll() {
return jdbcTemplate.query("select * from users", new UserRowMapper());
}
class UserRowMapper implements RowMapper<User> {
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
return user;
}
Here is my junit class
#Autowired
private UserRepository userRepository;
private EmbeddedDatabase db;
#Before
public void setUp() {
// db = new EmbeddedDatabaseBuilder().addDefaultScripts().build();
Object db = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).addScript("schema.sql")
.addScript("data.sql").build();
}
#Test
public void findAllUsers() {
List<User> users = userRepository.findAll();
System.out.println(users.get(0).getName());
assertNotNull(users);
assertTrue(!users.isEmpty());
}
when I run my junit I am getting the following error.
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userRepository': Unsatisfied dependency expressed through field 'jdbcTemplate': Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Tomcat.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceInitializer': Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.UncategorizedScriptException: Failed to execute database script; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
Do I have to add any other dependency or download a database.
Since you want to use In-memory database which is H2 so you shouldn't be having MySql dependecy in pom.xml and mysql related properties in application.properties.
Remove the MySql connector dependency from pom.xml
<!--Delete This-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.36</version>
And delete all the properties in application.properties file.
Keep it blank, Spring boot will auto-configure these properties for H2 database.
Checkout the complete Spring Boot JDBC Demo project in the GitHub repository.

Elasticsearch-Spark serialization not working with inner classes

Elasticsearch/Spark serialization does not appear to play well with nested types.
For example:
public class Foo implements Serializable {
private List<Bar> bars = new ArrayList<Bar>();
// getters and setters
public static class Bar implements Serializable {
}
}
List<Foo> foos = new ArrayList<Foo>();
foos.add( new Foo());
// Note: Foo object does not contain nested Bar instances
SparkConf sc = new SparkConf(); //
sc.setMaster("local");
sc.setAppName("spark.app.name");
sc.set("spark.serializer", KryoSerializer.class.getName());
JavaSparkContext jsc = new JavaSparkContext(sc);
JavaRDD javaRDD = jsc.parallelize(ImmutableList.copyOf(foos));
JavaEsSpark.saveToEs(javaRDD, INDEX_NAME+"/"+TYPE_NAME);
The above code above works, and documents of type Foo will be indexed within Elasticsearch.
The issue arises when the bars list in a Foo object is not empty, for instance:
Foo = new Foo();
Bar = new Foo.Bar();
foo.getBars().add(bar);
Then, when indexing to Elasticsearch, the following exception is thrown:
org.elasticsearch.hadoop.serialization.EsHadoopSerializationException:
Cannot handle type [Bar] within type [class Foo], instance [Bar ...]]
within instance [Foo#1cf628a]
using writer [org.elasticsearch.spark.serialization.ScalaValueWriter#4e635d]
at org.elasticsearch.hadoop.serialization.builder.ContentBuilder.value(ContentBuilder.java:63)
at org.elasticsearch.hadoop.serialization.bulk.TemplatedBulk.doWriteObject(TemplatedBulk.java:71)
at org.elasticsearch.hadoop.serialization.bulk.TemplatedBulk.write(TemplatedBulk.java:58)
at org.elasticsearch.hadoop.rest.RestRepository.writeToIndex(RestRepository.java:148)
at org.elasticsearch.spark.rdd.EsRDDWriter.write(EsRDDWriter.scala:47)
at org.elasticsearch.spark.rdd.EsSpark$$anonfun$saveToEs$1.apply(EsSpark.scala:68)
at org.elasticsearch.spark.rdd.EsSpark$$anonfun$saveToEs$1.apply(EsSpark.scala:68)
at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:61)
at org.apache.spark.scheduler.Task.run(Task.scala:64)
at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:203)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
These are the relevant Maven dependencies
<dependency>
<groupId>com.sksamuel.elastic4s</groupId>
<artifactId>elastic4s_2.11</artifactId>
<version>1.5.5</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch-hadoop-cascading</artifactId>
<version>2.1.0.Beta4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch-spark_2.10</artifactId>
<version>2.1.0.Beta4</version>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-xml</artifactId>
<version>2.11.0-M4</version>
</dependency>
What is the correct way to index when using nested types with ElasticSearch and Spark?
Thanks
A solution could be to build a json from the object you're trying to save, using for example Json4s.
In this case your "JavaEsSpark" RDD would be a RDD of strings.
Then you simply have to call
JavaEsSpark.saveJsonToEs...
instead of
JavaEsSpark.saveToEs...
This workaround helped me save countless hours trying to figure out a way to Serialize nested maps.
Looking at the ScalaValueWriter & JdkValueWriter code we can see that only certain types are directly supported. Most likely the inner class is not a JavaBean or other supported type.
One day ScalaValueWriter & JdkValueWriter will possibly support user defined types (like Bar in our example), other than just Java types like String, int, etc.
In the meantime, there is the following workaround. Instead of having Foo expose a List of Bar objects, internally transform the List to a Map<String, Object> and expose that.
Something like this:
private List<Map<String, Object>> bars= new ArrayList<Map<String, Object>>();
public List<Map<String, Object>> getBars() {
return bars;
}
public void setBars(List<Bar> bars) {
for (Bar bar: bars){
this.bars.add(bar.getAsMap());
}
}
i suggest working with com.google.gson.Gson;
String foosJson = new Gson().toJson(foos );
then ,
Map map = new HashMap<> ();
...
...
JavaRDD<Map<String,?>> javaRDD= sc.parallelize(ImmutableList.of(map));
JavaEsSpark.saveToEs ( javaRDD, INDEX_NAME+"/"+TYPE_NAME );

Resources