vaadin23 ComboBox.setValue failing with conversion error - combobox

I'm using vaadin23 to display a list of timezones in a combobox using a binder:
ComboBox<ZoneId> timezoneField = new ComboBox<>();
timezoneField.setItemLabelGenerator(zoneId -> zoneId.getId());
timezoneField.setItems(Timezones.getZones());
binder.forField(timezoneField)
.bind("timezone");
final var registration = new Registration(member, organisation);
binder.setBean(registration);
The above code seems to work fine until I try to set a default value:
UI.getCurrent().getPage().retrieveExtendedClientDetails(extendedClientDetails ->
{
int timezoneOffest = extendedClientDetails.getRawTimezoneOffset();
var possibleZones = Conversions.timezonesFromOffset(timezoneOffest);
if (possibleZones.size() != 0)
{
// this line throws
this.timezoneField.setValue((ZoneId) possibleZones.get(0));
}
});
possibleZones returns a list of ZoneId but in the debugger they show as ZoneRegion's.
I think this is OK as ZoneRegion is a type of ZoneId.
I do the cast when calling setValue just in case.
So when I call:
this.timezoneField.setValue((ZoneId) possibleZones.get(0));
The following error is thrown:
com.vaadin.flow.data.binder.BindingException: An exception has been thrown inside binding logic for the field element [suppress-template-warning='', _inputElementValue='', _clientSideFilter='false', selectedItem='null', invalid='false', pageSize='50', itemValuePath='key', itemIdPath='key', value='1']
at com.vaadin.flow.data.binder.Binder$BindingImpl.execute(Binder.java:1542) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.binder.Binder$BindingImpl.doConversion(Binder.java:1286) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.binder.Binder$BindingImpl.doValidation(Binder.java:1306) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.binder.Binder$BindingImpl.validate(Binder.java:1247) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.binder.Binder.lambda$doWriteIfValid$3(Binder.java:2245) ~
...
at com.vaadin.flow.data.binder.Binder.doWriteIfValid(Binder.java:2246) ~[flow-data-23.0.4.jar:23.0.4]
....
com.vaadin.flow.component.internal.AbstractFieldSupport.setValue(AbstractFieldSupport.java:135) ~[flow-server-23.0.4.jar:23.0.4]
at com.vaadin.flow.component.AbstractField.setValue(AbstractField.java:181) ~[flow-server-23.0.4.jar:23.0.4]
at com.vaadin.flow.component.combobox.ComboBox.setValue(ComboBox.java:396) ~[vaadin-combo-box-flow-23.0.5.jar:?]
at dev.onepub.ui.views.noauth.RegistrationView.lambda$4(RegistrationView.java:171) ~[classes/:?]
...
caused by:
Caused by: java.lang.ClassCastException: Cannot cast java.time.ZoneRegion to java.lang.String
at java.lang.Class.cast(Class.java:3889) ~[?:?]
at com.vaadin.flow.data.converter.Converter.lambda$from$957be2b0$1(Converter.java:104) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.binder.Result.of(Result.java:90) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.converter.Converter.lambda$from$b652e465$1(Converter.java:104) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.converter.Converter$1.convertToModel(Converter.java:130) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.converter.Converter$2.lambda$convertToModel$6b579330$1(Converter.java:165) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.binder.SimpleResult.flatMap(SimpleResult.java:65) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.binder.ValidationResultWrap.flatMap(ValidationResultWrap.java:67) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.converter.Converter$2.convertToModel(Converter.java:165) ~[flow-data-23.0.4.jar:23.0.4]
at com.vaadin.flow.data.binder.Binder$BindingImpl.lambda$doConversion$0(Binder.java:1288) ~[flow-data-23.0.4.jar:23.0.4]
I note that the conversion is complaining about converting ZoneRegion to String.
Given the combobox is of type ZoneId and has a ItemLabelGenerator I dont' see why this error can be generated?

So the answer was to create a convertor.
It still doesn't make sense to my why setValue needs a convertor to work given that it is using the same type as the combo box is storing.
For the record here is the convertor:
package dev.onepub.fields.converters;
import java.time.ZoneId;
import com.vaadin.flow.data.binder.Result;
import com.vaadin.flow.data.binder.ValueContext;
import com.vaadin.flow.data.converter.Converter;
public class ZoneIdToStringConverter implements Converter<ZoneId, String>
{
private static final long serialVersionUID = 1L;
#Override
public Result<String> convertToModel(ZoneId fieldValue, ValueContext context)
{
// Converting to the field type should always succeed,
// so there is no support for returning an error Result.
if (fieldValue == null)
{
return Result.ok(ZoneId.systemDefault().getId());
}
return Result.ok(fieldValue.getId());
}
#Override
public ZoneId convertToPresentation(String zoneId, ValueContext context)
{
// Produces a converted value or an error
// ok is a static helper method that creates a Result
if (zoneId == null)
return ZoneId.systemDefault();
else
return ZoneId.of(zoneId);
}
}
and to call it
ComboBox<ZoneId> timezoneField = new ComboBox<>();
timezoneField.setItemLabelGenerator(zoneId -> zoneId.getId());
timezoneField.setItems(Timezones.getZones());
binder.forField(timezoneField)
.withConverter(new ZoneIdToStringConverter())
.bind("timezone");
final var registration = new Registration(member, organisation);
binder.setBean(registration);

Related

Unable to Correctly Serialize RangeSet<Instant> with Flink Serialization System

I've implemented a RichFunction with following type:
RichMapFunction<GeofenceEvent, OutputRangeSet>
the class OutputRangeSet has a field of type:
com.google.common.collect.RangeSet<Instant>
When this pojo is serialized using Kryo I get null fields !
So far, I tried using a TypeInfoFactory<RangeSet>:
public class InstantRangeSetTypeInfo extends TypeInfoFactory<RangeSet<Instant>> {
#Override
public TypeInformation<RangeSet<Instant>> createTypeInfo(Type t, Map<String, TypeInformation<?>> genericParameters) {
TypeInformation<RangeSet<Instant>> info = TypeInformation.of(new TypeHint<RangeSet<Instant>>() {});
return info;
}
}
That annotate my field:
public class OutputRangeSet implements Serializable {
private String key;
#TypeInfo(InstantRangeSetTypeInfo.class)
private RangeSet<Instant> rangeSet;
}
Another solution (that doesn't work either) is registring a third party serializer:
env.getConfig().registerTypeWithKryoSerializer(RangeSet.class, ProtobufSerializer.class);
You can get the github project here:
https://github.com/elarbikonta/tonl-events
When you run the test you can see (in debug) that the rangeSet beans I get from my RichFunction has null fields, see test method com.tonl.apps.events.IsVehicleInZoneTest#operatorChronograph :
final RangeSet<Instant> rangeSet = resultList.get(0).getRangeSet(); // rangetSet.ranges = null !
Thanks for your help

Salesforce Test Class - System.NullPointerException: Attempt to de-reference a null object

Salesforce Test Class - Facing - System.NullPointerException: Attempt to de-reference a null object Error while trying to Run Test Class
APEX class
public class NewAccountCreation {
public Account account{get;set;}
public void save(){
Account acc = new Account();
// User enter values in vf page and we are capturing and creating account
acc.name = account.Name;
//acc.address = account.adress;
Insert acc;
}
}
Test Class
#isTest
public class TestNewAccountCreation {
#isTest static void TestNewAccountCreationMethod(){
NewAccountCreation testAccount = new NewAccountCreation ();
Account acc = new Account(Name='TestAcct');
Insert acc;
system.debug(''+acc);
testAccount.save();
System.assert([Select Id From Account].size()==1);
}
}
Error:
System.NullPointerException: Attempt to de-reference a null object
StackTrace:
Class.NewAccountCreation.save: line 6, column 1
Class.TestNewAccountCreation.TestNewAccountCreationMethod: line 9, column 1
You never set testAccount.account, so it's null. You do need to populate this variable, and you do not need to create and insert an Account in your test code. In fact, doing so will cause your assertion to fail.

Mybatis - Passing String[] to Oracle stored procedure

I have a Oracle SP with the parameters as below
create type stringArray as table of varchar2(30)
/
CREATE OR REPLACE PROCEDURE create_deliverable
(
in_p_name varchar2,
in_p_filename stringArray
)AS
ret_ID number;
BEGIN
...
END;
/
while the "file name" is a string array in Oracle.
The bean is defined as below:
#Data
public class BaseEntity {
private String name;
private String[] filename;
}
I want to pass the entire bean to Oracle stored procedure.
In my mapper.java
#Mapper
public interface BaseMapper {
void add(BaseEntity d);
}
In my BaseMapper.xml
<select id="add" statementType="CALLABLE" parameterType="BaseEntity">
call create_deliverable(
#{name},
#{filename,jdbcType=ARRAY, typeHandler=ArrayTypeHandler}
)
</select>
I attempted to write up a type handler to deal with the case. But I failed in the part to work it out.
Here's what's failed:
public class ArrayTypeHandler extends BaseTypeHandler<Object> {
#Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
throws SQLException {
Class<?> componentType = parameter.getClass().getComponentType();
String arrayTypeName = resolveTypeName(componentType);
Array array = ps.getConnection().createArrayOf(arrayTypeName, (Object[]) parameter);
ps.setArray(i, array);
array.free();
}
}
The failure is related to the part of "createArrayOf". It reads the arrayTypeName as VARCHAR, which is correct
Here's the error message:
Could not set parameters for mapping: ParameterMapping{property='filename', mode=IN, javaType=class java.util.ArrayList, jdbcType=ARRAY, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'}. Cause: org.apache.ibatis.type.TypeException: Error setting non null for parameter #2 with JdbcType ARRAY
Any input will be greatly appreciated.
Thanks
#ave has given the correct answer.
Just because I'm using springboot I'm here to recap the solution:
I'm using the default hikari pool & I didn't touch the datasource previously. It's autowired. It needs to be overwritten otherwise below error message would pop up
Cause: java.lang.ClassCastException: class
com.zaxxer.hikari.pool.HikariProxyConnection cannot be cast to class
oracle.jdbc.OracleConnection
(com.zaxxer.hikari.pool.HikariProxyConnection and
oracle.jdbc.OracleConnection are in unnamed module of loader 'app'
Here's my application.yml
spring: application:
name: tools datasource:
username: myuser
password: mypassword
url: jdbc:oracle:thin:#(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)
(PORT=1521))(CONNECT_DATA= (SERVICE_NAME=orclpdb)))
driver-class-name: oracle.jdbc.OracleDriver
Here's my datasource bean
import oracle.jdbc.pool.OracleDataSource;
#Value("${spring.datasource.username}")
String username;
#Value("${spring.datasource.password}")
String password;
#Value("${spring.datasource.url}")
String url;
#Bean
DataSource oracleDataSource() throws SQLException {
OracleDataSource dataSource = new OracleDataSource();
dataSource.setUser(username);
dataSource.setPassword(password);
dataSource.setURL(url);
return dataSource;
}
2)Here's my entity bean(I created new ones to not change the previous ones):
#Data
public class TestEntity {
private String name;
private String[] filename;
}
3)Here's my mapper java:
#Mapper
public interface TestMapper {
void add(TestEntity t);
}
Here's my mapper xml:
<select id="add" statementType="CALLABLE" parameterType="org.ssc.gss.entity.TestEntity">
call create_deliverable_test(
#{name,mode=IN},
#{filename,mode=IN,jdbcType=ARRAY,javaType=ArrayList, typeHandler=OracleStringArrayTypeHandler}
)
</select>
please note the "filename" parameter needs to be assigned to the typeHandler as mybatis default one won't work
5)the OracleStringArrayTypeHandler => Refers to ave's answer. There are a few more methods to be implemented but the key is the set parameter & I used
import oracle.jdbc.OracleConnection;
instead of
oracle.jdbc.driver.OracleDriver;
as the 2nd one has been deprecated
The Oracle stored procedure part:
create type stringArray as table of varchar2(30)
/
CREATE OR REPLACE PROCEDURE create_deliverable_test
(
in_p_name varchar2,
in_p_filename stringArray
)AS
ret_ID number;
BEGIN
...
END;
/
Thanks for #ave again. Without the help from #ave I couldn't move forward for days. I was thinking of use delimiter seperated string instead previously but absolutely string [] is much more advanced & convinient
Oracle's JDBC driver (as of version 19.8.0.0) does not support java.sql.Connection#createArrayOf() which is used by MyBatis' built-in ArrayTypeHandler, unfortunately.
So, you need to write a custom type handler.
I just tested and the following implementation worked.
package test;
import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import oracle.jdbc.OracleConnection;
public class OracleStringArrayTypeHandler extends BaseTypeHandler<String[]> {
#Override
public void setNonNullParameter(PreparedStatement ps, int i,
String[] parameter, JdbcType jdbcType) throws SQLException {
OracleConnection conn = ps.getConnection().unwrap(OracleConnection.class);
Array array = conn.createOracleArray("STRINGARRAY", parameter);
ps.setArray(i, array);
array.free();
}
...
And specify the type handler in the parameter reference.
call create_deliverable(
#{name},
#{filename,jdbcType=ARRAY,typeHandler=test.OracleStringArrayTypeHandler}
)

spring-data-mongodb bulkOps cant not serialize enum

I an enum field in my model,when I use find method to query some data with this field as condition, it return as wanted.
but, when I use bulkOps and excute an upsert operation,it tells me:
can't serialize class com.timanetworks.tpc.vehicle.alarm.domain.enums.FaultType
I am try to add name() method of enum to resolve this problem, and it's success! But I still can not understand why? does any one know?
my model is like this:
#Document(collection = "FaultSnapshot")
#TypeAlias("faultSnapshot")
public class FaultSnapshot extends BaseDocument {
private FaultType type;
private String vin;
private Integer faultLevel;
private Boolean isFault;
private Date time;
...setters and getters...
and FaultType is an enum:
public enum FaultType {
FAULT_EMS,
....
}
and this is find code:
Query query = new Query();
if (type != null) {
query.addCriteria(where("type").is(type));
}
Pageable pageable = new PageRequest(pageIndex - 1, pageSize);
Sort sort = new Sort(Sort.Direction.DESC, "time");
List<FaultHistory> histories = template.find(query.with(pageable).with(sort),
FaultHistory.class);
return new Page<>(count, histories);
this is bulkOps code:
public void upsertSnapshot(Collection<FaultSnapshot> snapshots) {
BulkOperations bulk = template.bulkOps(BulkOperations.BulkMode.UNORDERED, FaultSnapshot.class);
for (FaultSnapshot snapshot : snapshots) {
Query query = new Query();
query.addCriteria(where("vin").is(snapshot.getVin()));
query.addCriteria(where("type").is(snapshot.getType().name()));
Update update = new Update()
.set("isFault", snapshot.getFault())
.set("faultLevel", snapshot.getFaultLevel())
.set("time", snapshot.getTime())
.set("vin", snapshot.getVin())
.set("type", snapshot.getType().name());
bulk.upsert(query, update);
}
bulk.execute();
}
finally,this is the error stack:
Exception in thread "Thread-11" java.lang.IllegalArgumentException: can't serialize class com.timanetworks.tpc.vehicle.alarm.domain.enums.FaultType
at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:299)
at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:194)
at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:255)
at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:194)
at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:136)
at com.mongodb.DefaultDBEncoder.writeObject(DefaultDBEncoder.java:36)
at com.mongodb.OutMessage.putObject(OutMessage.java:289)
at com.mongodb.OutMessage.writeUpdate(OutMessage.java:180)
at com.mongodb.OutMessage.update(OutMessage.java:60)
at com.mongodb.DBCollectionImpl$Run$1.executeWriteProtocol(DBCollectionImpl.java:908)
at com.mongodb.DBCollectionImpl$Run$RunExecutor.executeWriteProtocol(DBCollectionImpl.java:1025)
at com.mongodb.DBCollectionImpl$Run$RunExecutor.execute(DBCollectionImpl.java:1016)
at com.mongodb.DBCollectionImpl$Run.executeUpdates(DBCollectionImpl.java:917)
at com.mongodb.DBCollectionImpl$Run.execute(DBCollectionImpl.java:859)
at com.mongodb.DBCollectionImpl.executeBulkWriteOperation(DBCollectionImpl.java:169)
at com.mongodb.DBCollection.executeBulkWriteOperation(DBCollection.java:1904)
at com.mongodb.DBCollection.executeBulkWriteOperation(DBCollection.java:1899)
at com.mongodb.BulkWriteOperation.execute(BulkWriteOperation.java:116)
at org.springframework.data.mongodb.core.DefaultBulkOperations.execute(DefaultBulkOperations.java:276)
at com.timanetworks.tpc.vehicle.alarm.dao.FaultSnapshotRepositoryImpl.***upsertSnapshot***(FaultSnapshotRepositoryImpl.java:39)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
This is actually a bug (DATAMONGO-1678) in Spring Data MongoDB. It has already been fixed for 1.9.12, 1.10.5 and 2.0.0.RC1.
At the time of writing none of the mentioned versions has been released.

Does Spring Expression Language support IN operator?

Does Spring Expression Language support IN operator? Similar to SQL IN clause.
public class Security {
private secTyp1;
public Security (String aSecTyp1) {
secTyp1 = aSecTyp1;
}
}
Security security = new Security("BOND");
StandardEvaluationContext context = new StandardEvaluationContext(security);
ExpressionParser parser = new SpelExpressionParser();
// This should return true
boolean result = parser.parseExpression("secTyp1 **IN** {'BOND','SWPI'}").getValue(context, Boolean.class);
// This should return false
result = parser.parseExpression("secTyp1 **IN** {'FUT','SWPI'}").getValue(context, Boolean.class);
I get the following exception
org.springframework.expression.spel.SpelParseException: EL1041E:(pos 8): After parsing a valid expression, there is still more data in the expression: 'IN'
at org.springframework.expression.spel.standard.InternalSpelExpressionParser.doParseExpression(InternalSpelExpressionParser.java:118)
at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:56)
at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:1)
at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpression(TemplateAwareExpressionParser.java:66)
at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpression(TemplateAwareExpressionParser.java:56)
at com.rules.AssignableSecurityRule.evaluateInCondition(AssignableSecurityRule.java:48)
at com.rules.AssignableSecurityRuleTest.testINCondition(AssignableSecurityRuleTest.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
It does not support the IN operator because you do not need it. You can use the Method invokation feature instead.
So, invoking .contains(..) on a list will do what you want.
You can try those two solutions
#Test
public void solutionOneTest() {
final Security security = new Security("BOND");
final EvaluationContext context = new StandardEvaluationContext(security);
Boolean contains = PARSER.parseExpression("{'BOND','SWPI'}.contains(#root.secTyp1)").getValue(context, Boolean.class);
Assert.assertTrue(contains);
contains = PARSER.parseExpression("{'FUT','SWPI'}.contains(#root.secTyp1)").getValue(context, Boolean.class);
Assert.assertFalse(contains);
}
#Test
public void solutionTwoTest() {
final Security security = new Security("BOND");
final EvaluationContext context = new StandardEvaluationContext();
context.setVariable("sec", security);
Boolean contains = PARSER.parseExpression("{'BOND','SWPI'}.contains(#sec.secTyp1)").getValue(context, Boolean.class);
Assert.assertTrue(contains);
contains = PARSER.parseExpression("{'FUT','SWPI'}.contains(#sec.secTyp1)").getValue(context, Boolean.class);
Assert.assertFalse(contains);
}
In addition to calling methods directly as in micfra's answer, SpEL also supports some pretty powerful collection filtering expressions as well. Collection Selection allows you to filter out a sublist of matching values, when you could then test for size:
!{'BOND','SWPI'}.?[#this == 'BOND'].empty
Where this is really handy is filtering lists of complex objects:
!securities.?[secTyp1 == 'BOND'].empty

Resources