Convert from Map to Set in Spring Cassandra - cassandra

I am trying to convert from Map to Set in Cassandra
#CassandraType(type = Name.SET)
private Map<Integer, Address> addresses;
Address is my UDT.
#UserDefinedType(value = "address")
public class Address
I am getting this error.
Can some one help me to fox this
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'session' defined in class path resource [org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Expected [1] typed arguments for property ['addresses'] of type ['interface java.util.Map']

Not sure how you intend to convert Set to a Map. Cassandra columns must be represented with their according data types in entity classes.
If you want to apply conversion then you need to handle this aspect within your code. Simply remove #CassandraType(type = Name.SET).

Related

Nomin automap causes infinite loop

I am using Nomin for mapping tasks. As taken from the documentation of Nomin it should be able to map fields with the same name by itself in case automapping has been activated. When activating it, it causes an infinite loop exception.
I have the following:
mappingFor a: CoinsOnMarketPlace, b: Coin
// automap() // when deactivated it works fine, when activated infinite loop
a.coin.name = b.name
a.coin.rank = b.rank
a.priceUSD = b.priceUSD // Could be automapped
a.priceBTC = b.priceBTC // Could be automapped
...
Exception:
org.nomin.core.NominException: ./net/hemisoft/ccm/repository/coinmarketcap2coin.groovy: Recursive mapping rule a = b causes infinite loop!
One thing worth adding regarding your use case - this Recursive mapping rule a = b causes infinite loop! exception is thrown because you use groovy classes in your mapping rule. Nomin uses ReflectionIntrospector and what's important:
It performs getting/setting properties using accessor methods which are called through the Java reflection mechanism. ReflectionIntrospector uses supplied NamingPolicy instance to determine accessor methods. JbNamingPolicy is used by default, this implementation cerresponds the JavaBeans convention. Its InstanceCreator named ReflectionInstanceCreator instantiates objects using Class.newInstance().
Source: http://nomin.sourceforge.net/introspectors.html
A simple Groovy class like:
class Entity {
String name
String somethingElse
}
gets compiled to a Java class that implements GroovyObject providing following methods:
public interface GroovyObject {
Object invokeMethod(String var1, Object var2);
Object getProperty(String var1);
void setProperty(String var1, Object var2);
MetaClass getMetaClass();
void setMetaClass(MetaClass var1);
}
In this case ReflectionInstanceCreator combined with automap() resolves following mappings:
a.property = b.property
and
a = b
where a = b mapping comes from MetaClass getMetaClass() getter method I suppose, because there is no mapping like a.metaClass = b.metaClass resolved. a.property = b.property gets resolved because of Object getProperty(String var1) method.
Solution
This problem can be solved by specifying explicitly ExplodingIntrospector for your mapping script that:
It performs getting/setting properties using a class field immediately through through the Java reflection mechanism and may be useful in case when domain object don't provide accessors for their properties. Supplied instance creator is ReflectionInstanceCreator.
Source: http://nomin.sourceforge.net/introspectors.html
All you have to do is to add
introspector exploding
right below mappingFor a: ..., b: ... header. For example:
import mypackage.Entity
import mypackage.EntityDto
mappingFor a: Entity, b: EntityDto
introspector exploding
automap()
a.test2 = b.test1
Tested with two Groovy classes, worked like a charm. Hope it helps.

Why am I getting a ConfigException 'Expected a comma separated list' error when creating a KafkaConsumer

I am trying to create a KafkaConsumer with the following Groovy code:
Properties props = new Properties()
props.put('bootstrap.servers', "$SERVER_ADDRESS:$port")
props.put('key.deserializer', StringDeserializer.name,)
props.put('value.deserializer', StringDeserializer.name)
return new KafkaConsumer<String, String>(props)
When I execute it, I am getting the following error:
org.apache.kafka.common.config.ConfigException: Invalid value
localhost:9092 for configuration bootstrap.servers: Expected a comma
separated list.
My code essentially matches the example creation of a Consumer from here
Why is my version not working?
In this case, the props object is expecting its values to be of type Object, and not only of type String. Therefore, when "$SERVER_ADDRESS:$port" is added as a property, it isn't coerced into a String and is stored as a GStringImpl.
The type checking done before creating a KafkaConsumer knows how to handle a String property value but not a GStringImpl and throws an error.
Changing the line to this works:
props.put('bootstrap.servers', "$SERVER_ADDRESS:$port".toString())
In version 0.10.0 using the KafkaConsumer or KafkaProducer, it expects an array of Strings. You are passing a String.

How can I ensure CassandraOperations.selectOneById() initializes all fields in the POJO?

I'm using Spring Data Cassandra 1.3.4.RELEASE to persist instances of a class that I have. The class is written in Groovy, but I don't think that really matters. I have implemented a CrudRepository, and I'm injecting an instance of CassandraOperations into the repo implementation class. I can insert, delete, and do most of the other operations successfully. However, there's a scenario I'm running into which breaks my test case. My entity class looks something like this:
#Table("foo")
class FooData {
#PrimaryKey("id")
long id
#Column("created")
long updated
#Column("name")
String name
#Column("user_data")
String userData
#Column("numbers")
List numberList = []
}
In my test case, I happened to only set a few fields like 'id' and 'updated' before calling CassandraOperations.insert(entity), so most of them were null in the entity instance at the time of insertion. But the numberList field was not null, it was an empty List. Directly after the insert(), I'm calling CassandraOperations.selectOneById(FooData.class, id). I get a FooData instance back, and the fields that were initialized when I saved it are populated with data. However, I was checking content equality in my test, and it failed because the empty list was not returned as an empty list in the POJO coming back from CassandraOperations.selectOneById(). It's actually null. I assume this may be some sort of Cassandra optimization. It seems to happen in the CGLIB code that instantiates the POJO/entity. Is this a known "feature"? Is there some annotation I can mark the 'numberList' field with to indicate that it cannot be null? Any leads are appreciated. Thanks.
In short
Cassandra stores empty collections as null and Spring Data Cassandra overwrites initialized fields.
Explanation
Cassandra list/set typed columns represent an empty collection as null. It does not matter whether the list/set (as viewed from Java/Groovy) was empty or null. Storing an empty list yields therefore in null. From here one can't tell whether the state was null or empty at the time saving the value.
Spring Data Cassandra overwrites all fields with values retrieved from the result set and so your pre-initialized fields is set to null.
I created a ticket DATACASS-266 to track the state of this issue.
Workaround
Spring Data uses setters if possible so you have a chance to intervene. A very simple null guard could be:
public void setMyList(List<Long> myList) {
if(myList == null){
this.myList = new ArrayList<>();
return;
}
this.myList = myList;
}
As important addition to mp911de answer you have to set #org.springframework.data.annotation.AccessType(AccessType.Type.PROPERTY) to make this solution work.

Value of selectManyCheckBox of type Calendar being cast as String [duplicate]

I am using <p:selectCheckboxMenu> on a List<Long>:
<p:selectCheckboxMenu value="#{bean.selectedItems}">
<f:selectItems value="#{bean.availableItems}" />
</p:selectCheckboxMenu>
private List<Long> selectedItems;
private Map<String, Long> availableItems;
When submitting the form and looping over the selected items as below,
for (int i = 0; i < selectedItems.size(); i++) {
Long id = selectedItems.get(i);
// ...
}
Then I get a class cast exception:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long
at com.example.Bean.submit(Bean.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.apache.el.parser.AstValue.invoke(AstValue.java:278)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:274)
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87)
... 27 more
The same problem occurs with <p:selectManyCheckbox>, <p:selectManyMenu>, <h:selectManyMenu>, etc. All multiple-selection components basically. It works fine in <p:selectOneMenu> and all other single-selection components on a single value Long property.
How is this caused and how can I solve it?
Your problem is caused by the following facts:
Java generics are compiletime syntactic sugar and completely absent during runtime.
EL expressions runs during runtime and not during compiletime.
HTTP request parameters are obtained as Strings.
Logical consequence is: EL doesn't see any generic type information. EL doesn't see a List<Long>, but a List. So, when you don't explicitly specify a converter, EL will after obtaining the submitted value as String set it unmodified in the List by reflection means. When you attempt to cast it to Long afterwards during runtime, you'll obviously face a ClassCastException.
The solution is simple: explicitly specify a converter for String to Long. You can use the JSF builtin LongConverter for this which has the converter ID javax.faces.Long. Other builtin converters are listed here.
<p:selectCheckboxMenu ... converter="javax.faces.Long">
Another solution without the need to explicitly specify the converter is to change List<T> type to a T[]. This way the EL will see the Long typed array and thus perform automatic conversion. But this possibly requires changes elsewhere in the model which may not be desirable.
private Long[] selectedItems;
In case you're using a complex object (javabean, entity, POJO, etc) as select item value instead of a standard type like Long for which JSF has builtin converters, then the same rules also apply. You only need to create a custom Converter and explicitly specify it in input component's converter attribute, or rely on forClass if you can use T[]. How to create such a converter is elaborated in Conversion Error setting value for 'null Converter'.

UISelectMany on a List<T> causes java.lang.ClassCastException: java.lang.String cannot be cast to T

I am using <p:selectCheckboxMenu> on a List<Long>:
<p:selectCheckboxMenu value="#{bean.selectedItems}">
<f:selectItems value="#{bean.availableItems}" />
</p:selectCheckboxMenu>
private List<Long> selectedItems;
private Map<String, Long> availableItems;
When submitting the form and looping over the selected items as below,
for (int i = 0; i < selectedItems.size(); i++) {
Long id = selectedItems.get(i);
// ...
}
Then I get a class cast exception:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long
at com.example.Bean.submit(Bean.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.apache.el.parser.AstValue.invoke(AstValue.java:278)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:274)
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87)
... 27 more
The same problem occurs with <p:selectManyCheckbox>, <p:selectManyMenu>, <h:selectManyMenu>, etc. All multiple-selection components basically. It works fine in <p:selectOneMenu> and all other single-selection components on a single value Long property.
How is this caused and how can I solve it?
Your problem is caused by the following facts:
Java generics are compiletime syntactic sugar and completely absent during runtime.
EL expressions runs during runtime and not during compiletime.
HTTP request parameters are obtained as Strings.
Logical consequence is: EL doesn't see any generic type information. EL doesn't see a List<Long>, but a List. So, when you don't explicitly specify a converter, EL will after obtaining the submitted value as String set it unmodified in the List by reflection means. When you attempt to cast it to Long afterwards during runtime, you'll obviously face a ClassCastException.
The solution is simple: explicitly specify a converter for String to Long. You can use the JSF builtin LongConverter for this which has the converter ID javax.faces.Long. Other builtin converters are listed here.
<p:selectCheckboxMenu ... converter="javax.faces.Long">
Another solution without the need to explicitly specify the converter is to change List<T> type to a T[]. This way the EL will see the Long typed array and thus perform automatic conversion. But this possibly requires changes elsewhere in the model which may not be desirable.
private Long[] selectedItems;
In case you're using a complex object (javabean, entity, POJO, etc) as select item value instead of a standard type like Long for which JSF has builtin converters, then the same rules also apply. You only need to create a custom Converter and explicitly specify it in input component's converter attribute, or rely on forClass if you can use T[]. How to create such a converter is elaborated in Conversion Error setting value for 'null Converter'.

Resources