I'm using Spring Boot example to read the following from the properties file.
sub.region.data={\
AF: {'subRegionCd' : '34', 'subRegionName' : 'Southern Asia', 'subRegionDesc': '', 'status' : 'A'} \
}
I used below, but it doesn't works
#Value("#{${sub.region.data}}")
private Map<String, SubRegion> subRegionsMap;
SubRegion.java
public class SubRegion {
private String subRegionCd;
private String subRegionName;
private String subRegionDesc;
private String subRegionStatus;
}
I am getting below error
Caused by: org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.util.Collections$UnmodifiableMap' to required type 'java.util.Map'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.util.Collections$UnmodifiableMap' to required type 'com.xxxxxx.model.SubRegion': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:76) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1195) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
... 54 common frames omitted
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.util.Collections$UnmodifiableMap' to required type 'com.xxxxxx.model.SubRegion': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:262) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.TypeConverterDelegate.convertToTypedMap(TypeConverterDelegate.java:608) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:182) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:73) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
... 57 common frames omitted
Looks like you made a mistake after 'subRegionDesc',, I think you mean using colon, not a comma here
With spring boot I suggest you to use ConfigurationProperties, instead of #Value.
For example, in this case you have to:
put #EnableConfigurationProperties(SubRegionConfig.class) to one of your spring configuration class.
Create config class:
#ConfigurationProperties(prefix = "sub.region")
public static class SubRegionConfig {
private Map<String, SubRegion> data;
//getters and setters
}
Use .yml instead of .properties, like that:
sub:
region:
data:
AF:
subRegionCd: '34'
subRegionName: 'Southern Asia'
subRegionDesc: ''
subRegionStatus: 'A'
After that you can get every properties you want from SubRegionConfing
#Autowired
private SubRegionConfig subRegionConfig;
ConfigurationsProperties is more complex, but more flexible and preferrable to use in most cases.
The #{} operator sends data into spEL parser, what means it will try to convert data into its know types, based on spEL syntax as shown here in Spring Docs.
What happens is that the compiler is unable to convert any data into a SubRegion object:
Cannot convert value of type 'java.util.Collections$UnmodifiableMap'
to required type 'com.xxxxxx.model.SubRegion': no matching editors or
conversion strategy found
A simple workaround is to nest two Map Objects so you can populate your SubRegion objects after retrieving the values.
yourMap={'key1':{'nestedMapKey1:value', 'nestedMapKey2:value2'}}
In your .properties file with your code:
sub.region.data={'AF':{'subRegionCd':'34', 'subRegionName':'Southern Asia', 'subRegionDesc':'', 'status':'A'}}
and then, in your code:
#Value("#{${sub.region.data}}")
private Map<String, Map<String, String>> subRegionsMap;
// (...) handling the SubRegion
My System.out.println(subRegionMap.get("AF"); outputed:
{subRegionCd=34, subRegionName=Southern Asia, subRegionDesc=, status=A}
Related
I have a Spring Integration Flow starting with a SFTPOutboundGateway. It should get a file from a SFTP server. The file contains Event objects in JSON format. My configuration is:
#Bean
public DirectChannelSpec sftpGetInputChannel() {
return MessageChannels.direct();
}
#Bean
public QueueChannelSpec remoteFileOutputChannel() {
return MessageChannels.queue();
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(5000));
return pollerMetadata;
}
#Bean
public IntegrationFlow sftpGetFlow(TransferContext context) {
return IntegrationFlows.from("sftpGetInputChannel")
.handle(Sftp.outboundGateway(sftpSessionFactory(context.getChannel()),
AbstractRemoteFileOutboundGateway.Command.GET, "payload")
.remoteDirectoryExpression(context.getRemoteDir())
.localDirectory(new File(context.getLocalDir()))
.localFilenameExpression(context.getLocalFilename())
)
.channel("remoteFileOutputChannel")
.transform(Transformers.fromJson(Event[].class))
.get();
}
To get a file from the SFTP server I send a message to the gateway's input channel "sftpGetInputChannel":
boolean sent = sftpGetInputChannel.send(new GenericMessage<>(env.getRemoteDir() + "/" + env.getRemoteFilename()));
An SpelEvaluationException is thrown when trying to interprete the given filename. Both fields, remoteDir and remoteFilename are of type String:
org.springframework.messaging.MessageHandlingException: error occurred in message handler [bean 'sftpGetFlow.sftp:outbound-gateway#0' for component 'sftpGetFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#0'; defined in: 'class path resource [com/harry/potter/job/config/SftpConfiguration.class]'; from source: 'bean method sftpGetFlow']; nested exception is org.springframework.messaging.MessagingException: Failed to execute on session; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1001E: Type conversion problem, cannot convert from org.springframework.messaging.support.GenericMessage<?> to java.lang.String
at org.springframework.integration.support.utils.IntegrationUtils.wrapInHandlingExceptionIfNecessary(IntegrationUtils.java:192)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:79)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:115)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:133)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:106)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:72)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:570)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:520)
at com.harry.potter.job.MyTask.getFile(MyEventsTask.java:170)
at com.harry.potter.job.myTask.runAsTask(MyEventsTask.java:112)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:836)
Caused by: org.springframework.messaging.MessagingException: Failed to execute on session; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1001E: Type conversion problem, cannot convert from org.springframework.messaging.support.GenericMessage<?> to java.lang.String
at org.springframework.integration.file.remote.RemoteFileTemplate.execute(RemoteFileTemplate.java:448)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.doGet(AbstractRemoteFileOutboundGateway.java:680)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.handleRequestMessage(AbstractRemoteFileOutboundGateway.java:584)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:134)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:62)
... 21 common frames omitted
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1001E: Type conversion problem, cannot convert from org.springframework.messaging.support.GenericMessage<?> to java.lang.String
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:75)
at org.springframework.expression.common.ExpressionUtils.convertTypedValue(ExpressionUtils.java:57)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:377)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.generateLocalFileName(AbstractRemoteFileOutboundGateway.java:1316)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.get(AbstractRemoteFileOutboundGateway.java:1081)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.lambda$doGet$6(AbstractRemoteFileOutboundGateway.java:681)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway$$Lambda$30183/0x0000000000000000.doInSession(Unknown Source)
at org.springframework.integration.file.remote.RemoteFileTemplate.execute(RemoteFileTemplate.java:439)
... 25 common frames omitted
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.messaging.support.GenericMessage<?>] to type [java.lang.String]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195)
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:70)
... 32 common frames omitted
What do I wrong?
Okay, the problem is not within the message to get the file but in the configuration of the gateway:
.localFilenameExpression(context.getLocalFilename())
Just to set the local filename as expression is not allowed because dots - often part of the filename - is a SpEL delimiter to separate bean from method name. Hence the expression becomes invalid.
Possible expression would be:
.localFilenameExpression("#remoteFileName")
See its JavaDocs:
/**
* Specify a SpEL expression for local files renaming after downloading.
* #param localFilenameExpression the SpEL expression to use.
* #return the Spec.
*/
public S localFilenameExpression(String localFilenameExpression) {
And here is some code snippet how it works:
private String generateLocalFileName(Message<?> message, String remoteFileName) {
if (this.localFilenameGeneratorExpression != null) {
EvaluationContext evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory());
evaluationContext.setVariable("remoteFileName", remoteFileName);
return this.localFilenameGeneratorExpression.getValue(evaluationContext, message, String.class);
}
return remoteFileName;
}
The expression you can provide should somehow be in the context of the currently downloaded remote file. Not sure what is yours static context.getLocalFilename(). You can build a local file name based on the request message and that remoteFileName variable contexts.
See more in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/sftp.html#configuring-with-the-java-dsl-3
The sample over there is like this:
.localDirectoryExpression("'myDir/' + #remoteDirectory")
.localFilenameExpression("#remoteFileName.replaceFirst('sftpSource', 'localTarget')"))
My environment variable is a string with time 23:30 In my String Boot application I'd like to parse it automatically and set it the result to variable.
I tried like this
#Value("#{ java.time.LocalTime.parse('${NIGHT_START_TIME}')}")
private LocalTime NIGHT_START_TIME;
IntelliJ shows the error
Cannot resolve variable 'java'
Here is log
Unsatisfied dependency expressed through field 'NIGHT_START_TIME';
nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'LocalTime' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public?
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
If I read variable like this I see that value is correct
#Value("${NIGHT_START_TIME}")
private String NIGHT_START_TIME;
Any ideas how to make it work?
Try this
#Value("#{ T(java.time.LocalTime).parse('${NIGHT_START_TIME}')}")
private LocalTime NIGHT_START_TIME;
The way you reference a class is using T.
Check the documentation.
Have checked at Spring Boot 2.2.6.RELEASE:
application.yml
NIGHT_START_TIME: '23:30'
Service
#Value("${NIGHT_START_TIME}")
private LocalTime NIGHT_START_TIME;
Work perfect without any "knee bends":)
I used spring boot + jdbctemplate and I have to use multi datasource, e.g.
#Configuration
public class MultiDBConfig {
#Bean(name = "fooDb")
#ConfigurationProperties(prefix = "foo.datasource")
public DataSource fooDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "fooJdbcTemplate")
public JdbcTemplate fooJdbcTemplate(#Qualifier("fooDb") DataSource ds) {
return new JdbcTemplate(ds);
}
#Bean(name = "barDb")
#ConfigurationProperties(prefix = "bar.datasource")
public DataSource barDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "barJdbcTemplate")
public JdbcTemplate barJdbcTemplate(#Qualifier("barDb") DataSource ds) {
return new JdbcTemplate(ds);
}
}
when start my application, it failed and have below error info
Parameter 0 of method fooJdbcTemplate in com.example.multidatasourcedemo.MultiDBConfig required a single bean, but 3 were found:
- fooDb: defined by method 'fooDataSource' in class path resource [com/example/multidatasourcedemo/MultiDBConfig.class]
- barDb: defined by method 'barDataSource' in class path resource [com/example/multidatasourcedemo/MultiDBConfig.class]
- testDb: defined by method 'testDataSource' in class path resource [com/example/multidatasourcedemo/MultiDBConfig.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
But I obviously have used #Qualifier to identify the bean , e.g.
#Bean(name = "fooJdbcTemplate")
public JdbcTemplate fooJdbcTemplate(#Qualifier("fooDb") DataSource ds)
Why doesn't #Qualifier work here?
So I've done some debugging and found something which might explain what's happening. At this point I'm not sure if it's a bug (could be this one), but I have not been able to find any other documentation to clarify this either.
For reference this is spring-boot 1.5.4.
I started from the log, you can find below an excerpt, more specifically the line regarding DataSourceInitializer.init (below with ==> at the beginning):
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 3: fooDb,barDb,testDb
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1041) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:345) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1090) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
==> at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.init(DataSourceInitializer.java:77) ~[spring-boot-autoconfigure-1.5.4.RELEASE.jar:1.5.4.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:366) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:311) ~[spring-beans-4.3.9.RELEASE
...
What happens is, when initialising the data sources, spring-boot tries to initialise the DB as well, feature which is enabled by default according to the docs:
Spring JDBC has a DataSource initializer feature. Spring Boot enables it by default and loads SQL from the standard locations schema.sql and data.sql (in the root of the classpath).
This takes place in the #PostConstruct section of org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer:
#PostConstruct
public void init() {
if (!this.properties.isInitialize()) {
logger.debug("Initialization disabled (not running DDL scripts)");
return;
}
if (this.applicationContext.getBeanNamesForType(DataSource.class, false, false).length > 0) {
==> this.dataSource = this.applicationContext.getBean(DataSource.class);
}
if (this.dataSource == null) {
logger.debug("No DataSource found so not initializing");
return;
}
runSchemaScripts();
}
As you can see, it tries to get the DataSource to execute the DB initialisation using the class this.dataSource = this.applicationContext.getBean(DataSource.class); and since there are 3 instances and no primary, it fails as per the expected behaviour of getBean(class)
<T> T getBean(Class<T> requiredType) throws BeansException
Return the bean instance that uniquely matches the given object type, if any.
This method goes into ListableBeanFactory by-type lookup territory but may also be translated into a conventional by-name lookup based on the name of the given type. For more extensive retrieval operations across sets of beans, use ListableBeanFactory and/or BeanFactoryUtils.
Parameters:
requiredType - type the bean must match; can be an interface or superclass. null is disallowed.
Returns:
an instance of the single bean matching the required type
Throws:
NoSuchBeanDefinitionException - if no bean of the given type was found
==> NoUniqueBeanDefinitionException - if more than one bean of the given type was found
BeansException - if the bean could not be created
So, bottom line, this happens before even trying to autowire your #Qualifier("fooDb") bean in the method, and I believe you have at lease these 2 choices, and in both cases your #Qualifier will be taken into account at the time when your JdbcTemplate is created:
if you need to execute some scripts to initialise your DB, then use #Primary to indicate which DataSource could be used for the task
otherwise, you can disable this implicit feature by adding spring.datasource.initialize=false in your application.properties (see here a list of common properties that can be configured)
This can be caused by a few different things. In my case, I had the following situation:
Two Datasource beans being configured in two Java classes, but both given specific Bean IDs
One place a Datasource was being injected, but correctly annotated with a Qualifier
A SpringBootApplication that was correctly excluding DataSourceAutoConfiguration
However, the bug turned out to be: a second class had been annotated as a SpringBootApplication and that was starting up...lost among the logs.
So, if everything else looks correct: check if some other, unexpected, SpringBootApplication is starting up.
My goal is to make web calls and convert returned JSON to POJOs. I'm trying to use Jersey+Jackson for this but am getting exceptions when running.
My maven pom file includes the following dependencies -
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.6</version>
</dependency>
The code I use to make fetch some data is as follows -
Client client = ClientBuilder.newBuilder()
.register(JacksonFeature.class)
.build();
ClientResponse response = client.target(url).request(MediaType.APPLICATION_JSON).get(ClientResponse.class);
But the following exception is throw -
javax.ws.rs.ProcessingException: Error reading entity from input stream.
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:868)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:785)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:335)
...
...
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not find a deserializer for non-concrete Map type [map type; class javax.ws.rs.core.MultivaluedMap, [simple type, class java.lang.String] -> [collection type; class java.util.List, contains [simple type, class java.lang.String]]]
at org.codehaus.jackson.map.deser.StdDeserializerProvider._createAndCache2(StdDeserializerProvider.java:315)
at org.codehaus.jackson.map.deser.StdDeserializerProvider._createAndCacheValueDeserializer(StdDeserializerProvider.java:290)
at org.codehaus.jackson.map.deser.StdDeserializerProvider.findValueDeserializer(StdDeserializerProvider.java:159)
at org.codehaus.jackson.map.deser.std.StdDeserializer.findDeserializer(StdDeserializer.java:620)
at org.codehaus.jackson.map.deser.BeanDeserializer.resolve(BeanDeserializer.java:379)
at org.codehaus.jackson.map.deser.StdDeserializerProvider._resolveDeserializer(StdDeserializerProvider.java:407)
at org.codehaus.jackson.map.deser.StdDeserializerProvider._createAndCache2(StdDeserializerProvider.java:352)
at org.codehaus.jackson.map.deser.StdDeserializerProvider._createAndCacheValueDeserializer(StdDeserializerProvider.java:290)
at org.codehaus.jackson.map.deser.StdDeserializerProvider.findValueDeserializer(StdDeserializerProvider.java:159)
at org.codehaus.jackson.map.deser.StdDeserializerProvider.findTypedValueDeserializer(StdDeserializerProvider.java:180)
at org.codehaus.jackson.map.ObjectMapper._findRootDeserializer(ObjectMapper.java:2829)
at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:2699)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1315)
at org.codehaus.jackson.jaxrs.JacksonJsonProvider.readFrom(JacksonJsonProvider.java:419)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:257)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:229)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:149)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1124)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:853)
... 90 more
Caused by: java.lang.IllegalArgumentException: Can not find a deserializer for non-concrete Map type [map type; class javax.ws.rs.core.MultivaluedMap, [simple type, class java.lang.String] -> [collection type; class java.util.List, contains [simple type, class java.lang.String]]]
at org.codehaus.jackson.map.deser.BasicDeserializerFactory.createMapDeserializer(BasicDeserializerFactory.java:424)
at org.codehaus.jackson.map.deser.StdDeserializerProvider._createDeserializer(StdDeserializerProvider.java:380)
at org.codehaus.jackson.map.deser.StdDeserializerProvider._createAndCache2(StdDeserializerProvider.java:310)
... 108 more
Am I missing some setup to get this to work right?
I have tried the url via curl and browser and it returns JSON as expected.
What you need is a Response and not a ClientResponse:
javax.ws.rs.core.Response jsonResponse = client.target(url).request(MediaType.APPLICATION_JSON).get();
Then you can see what comes in your response (debugging is your friend here). Is it by any chance a map of some type? If it is, you can read it by doing e.g.
Map<SomeClassOfYours> entitiesFromResponse = jsonResponse.readEntity(new GenericType<Map<SomeClassOfYours>>() {});
If you've put a normal entity in the response you can simply do something like:
SomeClassOfYours entityFromResponse = jsonResponse.readEntity(SomeClassOfYours.class);
Edit: For this to work you'd also need to define SomeClassOfYours and put the corresponding fields, constructor, getters and setters in there.
Edit2: When in doubt you can always read the jsonResponse as a String.class and put it in a String variable.
Response jsonResponse = getClient().target(URI).request().get();
T result = jsonResponse.readEntity(type);
I'm trying to create a simple web-site, which hosts topics and comments. I've begun with topics, and created repository for them:
package com.myProject.mvc3.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface TopicRepository extends CrudRepository<Topic, Integer>{
public List<Topic> findAllByTopicTag(Tag currentTag);
}
And I've defined the path for my repository in the servlet-context.xml:
jpa:repositories base-package="com.myProject.mvc3.repository"
Now, I'd like to include the comments in my repository, and the following code doesn't work:
package com.myProject.mvc3.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface CommentRepository extends CrudRepository<Comment, Integer> {
public List<Comment> findTopicComments(Topic topic);
}
My project doesn't build even. Can you give me an advice, how to create repositories for more than one entities (Topic class and Comment class are declared with #Entity)?
What i'm faciong:
there is HDD pic on the TopicRepository class icon
there is a question mark on the CommentRepository class icon
error log (a huge one):
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'topicController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.epam.mvc3.service.CommentService com.epam.mvc3.controller.TopicController.commentService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'commentService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.epam.mvc3.repository.CommentRepository com.epam.mvc3.service.CommentService.commentRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'commentRepository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: No property find found for type class com.epam.mvc3.model.Comment
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293)
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290)
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:192)
org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:467)
org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:483)
org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:358)
org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:325)
org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)
javax.servlet.GenericServlet.init(GenericServlet.java:160)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:928)
com.springsource.insight.collection.tcserver.request.HttpRequestOperationCollectionValve.invoke(HttpRequestOperationCollectionValve.java:84)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:987)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:539)
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:298)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
java.lang.Thread.run(Thread.java:722)
Just add By in findTopicComments so that it became findByTopicComments. Of course, this will work only if Comment entity has topicComments field or it has topic field which, in turn, has comments field.
Btw, you don't need #Repository annotation on Spring-data-jpa repositories.
Actually, if your query name doesn't match pattern ^(find|read|get)(\\p{Upper}.*?)??By from this class then the following occurs:
any of the prefixes such as get, 'read' and find won't be stripped and will be considered as the part of a JPQL query to generate. In this case you will get an exception:
java.lang.IllegalArgumentException: No property find found for type class ....
if there's nothing to strip from your method name then it will be processed as if it had findBy prefix before.
Since this is not clear from docs I've created the issue in Spring Data Commons issue tracker.
Try to write your repositories in such way:
#Repository
public interface TopicRepository extends JpaRepository<Topic, Integer> >{
#Query("select topic from Topic topic where topic.topicTag.id=?1")
public List<Topic> findAllByTopicTag(int topicTagId);
}
#Repository
public interface CommentRepository extends JpaRepository<Comment, Integer> {
#Query("select comment from Comment comment where comment.topic.id=?1")
public List<Comment> findTopicComments(int topicId);
}
In this example I specify id of the entities as search criteria because usually they are used as foreign key. You can easily add additional search criteria if you need.
More details about query methods in Spring Data you can find in reference documentation.
Update
About your error stack trace - I think this error means that you don't specify your classes in persistent.xml.
There is also a way to not specify all classes in your persistent.xml -> look here and here for details.
Also with Spring you can easily configure your JPA project without persistent.xml file if you use full java config. For example:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean factoryBean =
new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[] {"com.dimasco.springjpa.domain"});
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
//vendorAdapter.setGenerateDdl(generateDdl)
factoryBean.setJpaVendorAdapter(vendorAdapter);
Properties additionalProperties = new Properties();
additionalProperties.put("hibernate.hbm2ddl.auto", "update");
factoryBean.setJpaProperties(additionalProperties);
return factoryBean;
}
As you can see in this example I simply define what packages to scan.