How can I validate an unbounded wildcards elements? - spring-boot

I'm writing a code with spring-boot-starter-web:2.1.6.RELEASE which transitively depends on hibernate-validator:6.0.17.
And I got following error.
java.lang.IllegalArgumentException: HV000116:
type is not a reference type: ? extends java.math.BigDecimal
from
default #NotNull BigDecimal divide(
#Size(min = 2, max = 2) #NotNull
List<#NotNull ? extends BigDecimal> positioned) {
...
}
Especially from <#NotNull ? extends BigDecimal>.
What did I do wrong?
Here comes the stacktraces
java.lang.IllegalArgumentException: HV000116: type is not a reference type: ? extends java.math.BigDecimal
at org.hibernate.validator.internal.util.Contracts.assertTrue(Contracts.java:73)
at org.hibernate.validator.internal.util.TypeHelper.getErasedReferenceType(TypeHelper.java:193)
at org.hibernate.validator.internal.metadata.core.MetaConstraints.addValueExtractorDescriptorForWrappedValue(MetaConstraints.java:82)
at org.hibernate.validator.internal.metadata.core.MetaConstraints.create(MetaConstraints.java:55)
at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.createTypeArgumentMetaConstraint(AnnotationMetaDataProvider.java:795)
at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.lambda$findTypeUseConstraints$2(AnnotationMetaDataProvider.java:783)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1654)
at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:274)
at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.findTypeUseConstraints(AnnotationMetaDataProvider.java:784)
at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.findTypeArgumentsConstraints(AnnotationMetaDataProvider.java:762)
at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.findTypeAnnotationConstraintsForExecutableParameter(AnnotationMetaDataProvider.java:716)
at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getParameterMetaData(AnnotationMetaDataProvider.java:429)
at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.findExecutableMetaData(AnnotationMetaDataProvider.java:300)
at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getMetaData(AnnotationMetaDataProvider.java:285)
at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getMethodMetaData(AnnotationMetaDataProvider.java:272)
at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.retrieveBeanConfiguration(AnnotationMetaDataProvider.java:134)
at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getBeanConfiguration(AnnotationMetaDataProvider.java:124)
at org.hibernate.validator.internal.metadata.BeanMetaDataManager.getBeanConfigurationForHierarchy(BeanMetaDataManager.java:232)
at org.hibernate.validator.internal.metadata.BeanMetaDataManager.createBeanMetaData(BeanMetaDataManager.java:199)
at org.hibernate.validator.internal.metadata.BeanMetaDataManager.getBeanMetaData(BeanMetaDataManager.java:166)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:265)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:233)
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:112)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)

Your usage is correct. It's probably an implementation bug I think.
Look at how hibernate resolve type reference TypeHelper#getErasedReferenceType:
public static Class<?> getErasedReferenceType(Type type) {
Contracts.assertTrue( isReferenceType( type ), "type is not a reference type: %s", type );
return (Class<?>) getErasedType( type );
}
isReferenceType does not check for wildcard type TypeHelper#isReferenceType:
private static boolean isReferenceType(Type type) {
return type == null
|| type instanceof Class<?>
|| type instanceof ParameterizedType
|| type instanceof TypeVariable<?>
|| type instanceof GenericArrayType;
}
So the assert failed when hibernate try to validate the content of list (caused of <#NotNull ? extends BigDecimal>)
But somehow the TypeHelper#getErasedType supports for wild card type:
public static Type getErasedType(Type type) {
...
if ( type instanceof WildcardType ) {
Type[] upperBounds = ( (WildcardType) type ).getUpperBounds();
return getErasedType( upperBounds[0] );
}
...
}
I will try to open an issue about this.

Related

Spring Boot QueryDSL BooleanExpression - "OR" condition depending on the value in table

I use Spring Boot and QueryDSL to combine a sql query and predicate. The problem is that I have to create a predicate to fetch data from table based on email BUT:
email can be in Freight.sender.email
OR in Freight.message.senderAddress
where Freight, Message, Sender are of course tables.
In table Freight we can have empty sender_id or message_id and depending on this I have to fetch rows by email from Freight.sender.email OR Freight.message.senderAddress (if Freight.sender is null)
Is it possible to create such a predicate that compares email from request query param with Freight.sender.email and only if Freight.sender.email doesn't exist, then my predicate shold search email in Freight.message.senderAddress
public Predicate build(Map<String, String> filters) {
return new OptionalBooleanBuilder(Expressions.asBoolean(true).isTrue())
.notNullAnd(qFreight.loadingAddress::containsIgnoreCase, filters.get(LOADING_ADDRESS))
.notNullAnd(qFreight.unloadingAddress::containsIgnoreCase, filters.get(UNLOADING_ADDRESS))
.notNullAnd(qFreight.loadingDate.eq(filters.get(LOADING_DATE) != null ? LocalDate.parse(filters.get(LOADING_DATE)) : now()), filters.get(LOADING_DATE))
.notNullAnd(qFreight.unloadingDate.eq(filters.get(UNLOADING_DATE) != null ? LocalDate.parse(filters.get(UNLOADING_DATE)) : now()), filters.get(UNLOADING_DATE))
//MY ATTEMPT - NOT WORKING:
.notNullAnd(qFreight.sender.email.eq(filters.get(SENDER_EMAIL)).or(qFreight.emailMessage.senderAddress.eq(SENDER_EMAIL)), filters.get(SENDER_EMAIL))
.build();
}
public class OptionalBooleanBuilder {
private BooleanExpression predicate;
public OptionalBooleanBuilder(BooleanExpression predicate) {
this.predicate = predicate;
}
public <T> OptionalBooleanBuilder notNullAnd(Function<T, BooleanExpression> expressionFunction, T value) {
if (nonNull(value)) {
return new OptionalBooleanBuilder(predicate.and(expressionFunction.apply(value)));
}
return this;
}
public BooleanExpression build() {
return predicate;
}
public <T>OptionalBooleanBuilder notNullAnd(BooleanExpression expression, T value) {
if(nonNull(value)){
return new OptionalBooleanBuilder(predicate.and(expression));
}
return this;
}
}
UPDATE
After suggestion from:
private Predicate addPredicate(OptionalBooleanBuilder builder, String email) {
if (nonNull(email)) {
return builder.notNullAnd(qFreight.sender.email.coalesce(qFreight.emailMessage.senderAddress.eq(email)).asBoolean(), email).build();
return builder.build();
}
I get error:
antlr.NoViableAltException: unexpected AST node: (
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2169) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2089) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:827) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:621) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:325) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:273) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:276) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:192) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
2020-12-24 00:45:35.535 ERROR 16068 --- [nio-9090-exec-8] p.a.m.s.filter.JwtAuthorizationFilter : Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: ( near line 3, column 52 [select freight
from pl.appwise.mtf.freight.domain.model.Freight freight
where ?1 = ?1 and freight.user.id = ?2 and coalesce(freight.sender.email, freight.emailMessage.senderAddress = ?3)
order by freight.loadingDate desc]; nested exception is java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: ( near line 3, column 52 [select freight
from pl.appwise.mtf.freight.domain.model.Freight freight
where ?1 = ?1 and freight.user.id = ?2 and coalesce(freight.sender.email, freight.emailMessage.senderAddress = ?3)
order by freight.loadingDate desc]
2020-12-24 00:45:37.906 INFO 16068 --- [ scheduling-1] ilAccoun
Use Freight.sender.email.coalesce(Freight.message.senderAddress). Freight.message.senderAddress will be NULL if Freight.message is NULL. For optional associations left outer joins are used by default, so this should cause no issue. Otherwise, explicitly use a left join yourself.

Serializer not compatible to org.apache.avro.util.Utf8

I was trying to do aggregation using kafkastreams, i was getting error as given below
Here is what i was doing:
KGroupedStream<String, Long> countrywiseAmount = ......;
KTable<String, CountSum> countrywiseAverageSum = countrywiseAmount
.aggregate(new Initializer<CountSum>() {
#Override
public CountSum apply() {
return new CountSum();
}
}, new Aggregator<String,Long,CountSum>() {
#Override
public CountSum apply(String country, Long amount, CountSum sumByCountry) {
sumByCountry.setCountry(country.toString());
sumByCountry.setCount(sumByCountry.getCount()+1);
sumByCountry.setSum(sumByCountry.getSum()+amount);
return sumByCountry;
}
}, Materialized.with(stringSerde, countSumSerde));
The error i am getting is as below
Caused by: org.apache.kafka.streams.errors.StreamsException: A
serializer (key:
org.apache.kafka.common.serialization.StringSerializer / value:
org.apache.kafka.common.serialization.LongSerializer) is not
compatible to the actual key or value type (key type:
org.apache.avro.util.Utf8 / value type: java.lang.Long). Change the
default Serdes in StreamConfig or provide correct Serdes via method
parameters.
at org.apache.kafka.streams.processor.internals.SinkNode.process(SinkNode.java:94)
at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:201)
at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:180)
at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:133)
at org.apache.kafka.streams.kstream.internals.KStreamFilter$KStreamFilterProcessor.process(KStreamFilter.java:43)
at org.apache.kafka.streams.processor.internals.ProcessorNode.process(ProcessorNode.java:117)
at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:201)
at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:180)
at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:133)
at org.apache.kafka.streams.kstream.internals.KStreamMap$KStreamMapProcessor.process(KStreamMap.java:42)
at org.apache.kafka.streams.processor.internals.ProcessorNode.process(ProcessorNode.java:117)
at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:201)
at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:180)
at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:133)
at org.apache.kafka.streams.processor.internals.SourceNode.process(SourceNode.java:87)
at org.apache.kafka.streams.processor.internals.StreamTask.process(StreamTask.java:363)
... 5 more
Caused by: java.lang.ClassCastException: org.apache.avro.util.Utf8 cannot be cast to java.lang.String
at org.apache.kafka.common.serialization.StringSerializer.serialize(StringSerializer.java:28)
at org.apache.kafka.common.serialization.Serializer.serialize(Serializer.java:62)
at org.apache.kafka.streams.processor.internals.RecordCollectorImpl.send(RecordCollectorImpl.java:162)
at org.apache.kafka.streams.processor.internals.RecordCollectorImpl.send(RecordCollectorImpl.java:103)
at org.apache.kafka.streams.processor.internals.SinkNode.process(SinkNode.java:89)
Any clue?
There is no issue with the KTable aggregation logic, but the issue with KGroupedStream<String, Long> countrywiseChkAmount, while creating it from KStream<String, AvroModel>, new KeyValue creation, the key should be converted from CharSequence to String. Thanks

MessageHandlingException on a filter when using spring-integration 5.1.3.RELEASE

So I'm trying to run a test that runs through the following flow:
#EnableBinding({Sink.class,Source.class})
public class MyFlow {
public #Bean IntegrationFlow myIntegrationFlow() {
return IntegrationFlows.from("input") //
.transform(new JsonToObjectTransformer()) //
.filter(new MessageDroolsFilter())
.........
get();
}
public class MessageDroolsFilter implements MessageSelector {
#Override
public boolean accept(Message<?> message) {
return true;
}
}
}
So in spring-integration-core-5.1.2.RELEASE everything runs fine.
I want to upgrade to 5.1.3.RELEASE and I get the following exception. I can't see why it impacts the flow.
org.springframework.messaging.MessageHandlingException:
nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1004E:
Method call: Method accept(com.example.MyClass)
cannot be found on type com.example.myIntegrationFlow$$Lambda$942/1009480482, failedMessage=GenericMessage
[payload=com.example.MyClasst#72bd8702[id=100, timestamp=1563801840597}]
at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:109)
at org.springframework.integration.filter.AbstractMessageProcessingSelector.accept(AbstractMessageProcessingSelector.java:62)
at org.springframework.integration.router.RecipientListRouter$Recipient.accept(RecipientListRouter.java:320)
at org.springframework.integration.router.RecipientListRouter.lambda$determineTargetChannels$0(RecipientListRouter.java:258)
at java.util.stream.ReferencePipeline$2$1.accept(Unknown Source)
Edit:
I have a router and I want route messages based on type of the object received.
private IntegrationFlow messagesFlow() {
return sf -> sf //
.routeToRecipients(routeMessages());
}
private Consumer<RecipientListRouterSpec> routeMessages() {
return sf -> sf
.recipientFlow(new GenericSelector<MyObject1>() {
#Override
public boolean accept(MyObject1 source) {
return source instanceof MyObject1;
}},
f -> f.transform(myTransformer)
.filter(new DiscardHeaderMessageFilter())
.handle(myHandler))
.recipientFlow(new GenericSelector<MyObject2>() {
#Override
public boolean accept(MyObject2 source) {
return source instanceof MyObject2;
}
}
.defaultOutputChannel(DISCARD_CHANNEL);
}
I still get the same error:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method accept(com.example.MyObject1) cannot be found on type com.example.MyFlow$2
at org.springframework.expression.spel.ast.MethodReference.findAccessorForMethod(MethodReference.java:225)
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:134)
at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:54)
at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:390)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:90)
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:114)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:365)
at org.springframework.integration.util.AbstractExpressionEvaluator.evaluateExpression(AbstractExpressionEvaluator.java:172)
at org.springframework.integration.util.AbstractExpressionEvaluator.evaluateExpression(AbstractExpressionEvaluator.java:160)
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.invokeExpression(MessagingMethodInvokerHelper.java:664)
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.invokeHandlerMethod(MessagingMethodInvokerHelper.java:655)
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.processInternal(MessagingMethodInvokerHelper.java:491)
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.process(MessagingMethodInvokerHelper.java:362)
at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:106)
Doesn't look like your problem is related to the mentioned .filter().
See stack trace carefully:
at org.springframework.integration.filter.AbstractMessageProcessingSelector.accept(AbstractMessageProcessingSelector.java:62)
at org.springframework.integration.router.RecipientListRouter$Recipient.accept(RecipientListRouter.java:320)
So, you have a routeToRecipients() somewhere and one of its recipient() doesn't fit expectations.
UPDATE
The error is expectable: a Recipient List Router consults with each recipient against the current message. When your message is MyObject1, the first one, .recipientFlow(new GenericSelector<MyObject1>(), works well because its method signature is compatible with object you call it. But when the same MyObject1 reaches the second one - .recipientFlow(new GenericSelector<MyObject2>() -, it can't call it because non-compatible type.
Fully unclear why would one does source instanceof MyObject1; in the method when argument is exactly MyObject1...
I would say that signature must be like this:
.recipientFlow(new GenericSelector<Object>() {
#Override
public boolean accept(Object source) {
return source instanceof MyObject1;
}}
I mean generic Object type compatible with any payload sent. It's fully unclear why your IDE didn't tell you that source instanceof MyObject2 is redundant since it is going always be true, when this method is called. Only the problem that this method fails against any other type when we call it via reflection, like in that case with SpEL.

UncategorizedKeyValueException trying to save RedisHash via repository with transaction support

Problem:
After enabling transaction support (redisTemplate.setEnableTransactionSupport(true))
I get UncategorizedKeyValueException trying to save #RedisHash in repository.
Everything works fine when transaction support is disabled.
Direct operations via RedisTemplate work fine with transaction support.
Stacktrace:
...
at org.lorem.LoremController.create(TestController.java:41)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
... 17 more
Caused by: java.lang.NullPointerException
at org.springframework.data.redis.core.RedisKeyValueAdapter.lambda$put$0(RedisKeyValueAdapter.java:236)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:171)
at org.springframework.data.redis.core.RedisKeyValueAdapter.put(RedisKeyValueAdapter.java:231)
at org.springframework.data.keyvalue.core.KeyValueTemplate.lambda$insert$0(KeyValueTemplate.java:165)
at org.springframework.data.keyvalue.core.KeyValueTemplate.execute(KeyValueTemplate.java:343)
... 56 more
Background:
I'm using spring-boot-starter-data-redis:2.0.4.RELEASE
Configuration:
#Bean
#Primary
StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setEnableTransactionSupport(true);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
RedisHash:
#RedisHash("test")
public class Test {
private String id;
private String field;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
}
Repository:
public interface TestRepository extends CrudRepository<Test, String> {
}
Thanks :)
Update:
Things break at the following line in RedisKeyValueAdapter:
boolean isNew = connection.del(objectKey) == 0;
del() returns null since it runs in a transaction. Doesn't repositories suppose to work with transaction support enabled?
Redis Repositories do not work with Redis transactions. This is explained here: https://jira.spring.io/browse/DATAREDIS-862
Redis Repositories do not work with Redis transactions as operations that change the underlying data are queued and executed at the end of the transaction. These commands return null. However, we require the outcome immediately at invocation time.

How to pull sub, sub objects with Spring WS and JAXB

I'm attempting to pull data from a SOAP service that I have no control over. The hierarchy contains ProductOrder -> ShipTo -> Item where there are one or more shipToes and one or more Items per shipto.
Their API uses a mock SQL like query language. I'm getting stack traces like the following when trying to pull data including the items. if I exclude item, I'm able to pull the ProductOrders along with ShipTo objects, but items is always an empty list.
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at
org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:779)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:760)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:747)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
edu.umich.oud.giftformatter.convioexport.Application.main(Application.java:39)
~[classes/:na] Caused by:
org.springframework.oxm.UncategorizedMappingException: Unknown JAXB
exception; nested exception is javax.xml.bind.JAXBException: Field
order for ShipTo.Item.ItemId does not match the schema definition for
record type ProductOrder at
org.springframework.oxm.jaxb.Jaxb2Marshaller.convertJaxbException(Jaxb2Marshaller.java:915)
~[spring-oxm-4.3.6.RELEASE.jar:4.3.6.RELEASE] at
edu.umich.oud.giftformatter.convioexport.CustJaxbUnMarshaller.unmarshal(CustJaxbUnMarshaller.java:37)
~[classes/:na] at
org.springframework.ws.support.MarshallingUtils.unmarshal(MarshallingUtils.java:62)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate$3.extractData(WebServiceTemplate.java:413)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:619)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:555)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:390)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:383)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
edu.umich.oud.giftformatter.convioexport.services.ConvioClient.queryInternal(ConvioClient.java:159)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.services.ConvioClient.query(ConvioClient.java:134)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.services.ProductOrderService.getProductOrders(ProductOrderService.java:87)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.services.ConvioService.load(ConvioService.java:82)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.Application.lambda$runner$0(Application.java:72)
~[classes/:na] at
org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:776)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] ... 4 common frames
omitted Caused by: javax.xml.bind.JAXBException: Field order for
ShipTo.Item.ItemId does not match the schema definition for record
type ProductOrder ... 17 common frames omitted
The product order service contains a method like so:
public List<ProductOrderObj> getProductOrders(final Date startDate, final Date endDate) {
final String query = String.format("SELECT siteId,orderId,transactionId,purchaseAmount,taxDeductibleValue,\n" +
"shippingCharge,additionalDonation,discountAmount,discountCode,\n" +
"creationDate,createdBy,modifyDate,lastChangeBy,storeId,payment,\n" +
"purchaser,interactionSource,shipTo,\n" +
"receiptNumber,shipTo.item FROM ProductOrder where creationDate > %s and creationDate < %s",
convertDate(startDate), convertDate(endDate));
log.info("query is " + query);
final Session session = convioClient.startSession();
final ArrayList<ProductOrderObj> events = new ArrayList<>();
for (int page = 1; page < 100; page++) {
final List<? extends RecordObj> items = convioClient.query(session, page, ConvioConfiguration.MAX_DOWNLOADS_PER_REQUEST, query);
if (items.size() < ConvioConfiguration.MAX_DOWNLOADS_PER_REQUEST) {
events.addAll((List<ProductOrderObj>) items);
break;
}
events.addAll((List<ProductOrderObj>) items);
}
return events;
}
Which in turn calls the convioService.query method that effectively does this
private List<? extends RecordObj> queryInternal(final Session session, final
int page, final int pageSize, final String q) {
// setup query
final Query query = new Query();
query.setPage(BigInteger.valueOf(page));
query.setPageSize(BigInteger.valueOf(pageSize));
query.setQueryString(q);
log.trace(q);
// perform query
try {
final Object obj = getWebServiceTemplate().marshalSendAndReceive(query,
new SoapActionExecutionIdCallback(session));
final QueryResponse response = (QueryResponse) obj;
if (response != null) {
log.debug("Response was a " + response.getClass().getName());
return response.getRecord();
}
} catch (final Exception e) {
log.error(e.getMessage());
throw e;
}
throw new NullPointerException("response was null");
}
There seemed to be two issues causing this not to work:
Bad field definition for the child object. shipTo.items vs shipTo.Items
Disabling validation of the dtd in the marshaller/unmarshaller

Resources