Method with #JmsListener not executed when setting custom message converter - converters

Hy,
Having these three classes I have found a problem:
package xpadro.spring.jms;
import javax.jms.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MarshallingMessageConverter;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
#SpringBootApplication
public class JmsJavaconfigApplication {
public static void main(String[] args) {
SpringApplication.run(JmsJavaconfigApplication.class, args);
}
#Bean
public Jaxb2Marshaller jaxb2Marshaller(){
Jaxb2Marshaller jaxb = new Jaxb2Marshaller();
Class array[] = new Class[1];
array[0]= xpadro.spring.jms.model.Order.class;
jaxb.setClassesToBeBound(array);
return jaxb;
}
#Bean
#Autowired
public MarshallingMessageConverter marshallingMessageConverter( Jaxb2Marshaller marshaller){
MarshallingMessageConverter mc = new MarshallingMessageConverter();
mc.setMarshaller(marshaller);
mc.setUnmarshaller(marshaller);
return mc;
}
#Bean
#Autowired
public JmsTemplate init(MarshallingMessageConverter messageConverter,ConnectionFactory connectionFactory){
JmsTemplate template = new JmsTemplate();
template.setMessageConverter(messageConverter);
template.setConnectionFactory(connectionFactory);
return template;
}
}
Next, the model:
package xpadro.spring.jms.model;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "order")
#XmlAccessorType(XmlAccessType.FIELD)
public class Order implements Serializable {
private static final long serialVersionUID = -797586847427389162L;
#XmlElement(required = true)
private String id="";
public Order() {
}
public Order(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
Next, the listener:
package xpadro.spring.jms.listener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import xpadro.spring.jms.model.Order;
import xpadro.spring.jms.service.StoreService;
#Component
public class SimpleListener {
private final StoreService storeService;
#Autowired
public SimpleListener(StoreService storeService) {
this.storeService = storeService;
}
#JmsListener(destination = "simple.queue")
public void receiveOrder(Order order) {
storeService.registerOrder(order);
}
}
The class that sends messages:
package xpadro.spring.jms.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
import xpadro.spring.jms.model.Order;
#Service
public class ClientServiceImpl implements ClientService {
private static final String SIMPLE_QUEUE = "simple.queue";
private static final String IN_QUEUE = "in.queue";
private final JmsTemplate jmsTemplate;
#Autowired
public ClientServiceImpl(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
#Override
public void addOrder(Order order) {
//MessageRegistered with a bean
jmsTemplate.convertAndSend(SIMPLE_QUEUE, order);
}
#Override
public void registerOrder(Order order) {
//MessageRegistered with a bean
jmsTemplate.convertAndSend(IN_QUEUE, order);
}
}
The point is that listener(method receiveOrder()) is not executed when I set a custom message converter in the class JmsJavaconfigApplication. When I dont set it, spring boot sets a SimpleMessageConverter and the listener is executed.
Any wrong or missed configuration?
Thanks

You need to provide an marshalling converter to the #JmsListener to unmarshall the message, by configuring the listener container factory see the documentation.

Related

TransactionSynchronizationManager.isCurrentTransactionReadOnly() is always false

I want to implement a hibernate-based approach for developing read-only DB.
I have implemented the following code but when it comes to executing the
protected Object determineCurrentLookupKey() {
event though the request comes from the #Transactional(read-only=true) it always return "read-only=false" , Why transaction does not successfully process this ? Thanks.
DatabaseContextHolder.java
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class DatabaseContextHolder {
private static final Log log = LogFactory.getLog(DatabaseContextHolder.class);
private static final ThreadLocal<DatabaseEnvironment> CONTEXT = new ThreadLocal<>();
public static void set(DatabaseEnvironment databaseEnvironment) {
CONTEXT.set(databaseEnvironment);
}
public static DatabaseEnvironment getEnvironment() {
log.info("DB Environment : " + CONTEXT.get());
return CONTEXT.get();
}
public static void reset() {
CONTEXT.set(DatabaseEnvironment.WRITABLE);
}
}
DatabaseEnvironment.java
public enum DatabaseEnvironment {
WRITABLE,
READONLY
}
TransactionRoutingDataSource.java
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.lang.Nullable;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import io.api.readWriteRoutingHibernate.DataSourceConfiguration;
import io.api.readWriteRoutingHibernate.context.DatabaseEnvironment;
public class TransactionRoutingDataSource
extends AbstractRoutingDataSource {
private static final Log log = LogFactory.getLog(DataSourceConfiguration.class);
#Nullable
#Override
protected Object determineCurrentLookupKey() {
log.info("DatabaseEnvironment.READONLY : " + TransactionSynchronizationManager
.isCurrentTransactionReadOnly() );
return TransactionSynchronizationManager
.isCurrentTransactionReadOnly() ?
DatabaseEnvironment.READONLY :
DatabaseEnvironment.WRITABLE;
}
}
DataSourceConfiguration.java
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.zaxxer.hikari.HikariDataSource;
import io.api.readWriteRoutingHibernate.context.DatabaseEnvironment;
import io.api.readWriteRoutingHibernate.datasource.TransactionRoutingDataSource;
#Configuration
public class DataSourceConfiguration {
private static final Log log = LogFactory.getLog(DataSourceConfiguration.class);
#Value("${spring.datasource.master.url}")
private String mstUrl;
#Value("${spring.datasource.master.username}")
private String mstUsername;
#Value("${spring.datasource.master.password}")
private String mstPassword;
#Value("${spring.datasource.slave.url}")
private String slaveUrl;
#Value("${spring.datasource.slave.username}")
private String slaveUsername;
#Value("${spring.datasource.slave.password}")
private String slavePassword;
#Bean
public DataSource dataSource() {
TransactionRoutingDataSource masterSlaveRoutingDataSource = new TransactionRoutingDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseEnvironment.WRITABLE, masterDataSource());
targetDataSources.put(DatabaseEnvironment.READONLY, slaveDataSource());
masterSlaveRoutingDataSource.setTargetDataSources(targetDataSources);
// Set as all transaction point to master
masterSlaveRoutingDataSource.setDefaultTargetDataSource(masterDataSource());
return masterSlaveRoutingDataSource;
}
public DataSource slaveDataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setJdbcUrl(slaveUrl);
hikariDataSource.setUsername(slaveUsername);
hikariDataSource.setPassword(slavePassword);
return hikariDataSource;
}
public DataSource masterDataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setJdbcUrl(mstUrl);
hikariDataSource.setUsername(mstUsername);
hikariDataSource.setPassword(mstPassword);
return hikariDataSource;
}
protected Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty(
"hibernate.connection.provider_disables_autocommit",
Boolean.TRUE.toString()
);
return properties;
}
}

Reactive way of reading YAML with Jackson using Spring boot webflux

The yamlObjectMapper in configuration
#Bean
public ObjectMapper yamlObjectMapper() {
ObjectMapper yamlObjectMapper = new ObjectMapper(new YAMLFactory().disable(YAMLGenerator.Feature
.WRITE_DOC_START_MARKER));
yamlObjectMapper.findAndRegisterModules();
return yamlObjectMapper;
}
The Service to parse yaml file
#Service
public class CustomerService {
#Autowired
#Qualifier("yamlObjectMapper")
private ObjectMapper yamlObjectMapper;
public Customer get() {
try {
InputStream inputStream = ResourceUtils.getURL("classpath:/files/test.yaml").openStream();
return yamlObjectMapper.readValue(inputStream, Customer.class);
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
#Data
public static class Customer {
private String name;
private String surname;
private String email;
}
}
I guess IO operations are blocking, how this can be done using reactive way?
I would rather use configuration binding since probably you need to read it once.
package com.vob.webflux.webfilter.controller;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
#Component
#PropertySource(value = "classpath:config.yml", factory= YamlPropertySourceFactory.class)
#Getter
public class YamlFooProperties {
#Value("${test}")
private String test;
}
Factory
package com.vob.webflux.webfilter.controller;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import java.io.IOException;
import java.util.Properties;
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource)
throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(encodedResource.getResource());
Properties properties = factory.getObject();
return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
}
}
Source factory from

#Autowired Repository in ConstraintValidator null when used from CommandLineRunner

I'm trying to set up a Spring-Data-Rest App using Spring Boot 1.5.9.RELEASE. I have a ConstraintValidator set up like this:
UniqueEmail.java:
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.NotNull;
import java.lang.annotation.*;
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = {UniqueEmailValidator.class})
#NotNull
#ReportAsSingleViolation
public #interface UniqueEmail {
Class<?>[] groups() default {};
String message() default "{eu.icarus.momca.backend.domain.validation.UniqueEmail.message}";
Class<? extends Payload>[] payload() default {};
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
static #interface List {
UniqueEmail[] value();
}
}
UniqueEmailValidator.java:
import eu.icarus.momca.backend.domain.repository.AccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
#Autowired
private AccountRepository accountRepository;
#Override
public void initialize(UniqueEmail annotation) {
}
#Override
public boolean isValid(String email, ConstraintValidatorContext context) {
return accountRepository.findByEmail(email) == null;
}
}
I've also set up listeners for the beforeCreate and beforeSave repository methods:
RestConfiguration.java:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
#Configuration
public class RestConfiguration extends RepositoryRestConfigurerAdapter {
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", localValidatorFactoryBean());
validatingListener.addValidator("beforeSave", localValidatorFactoryBean());
}
#Bean
#Primary
LocalValidatorFactoryBean localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
}
My problem:
The validator seems to work when the repository methods are called through REST endpoints, however I also try to set up some stuff in the connected database via a CommandLineRunner that uses the same repository. When the calls to the repository are validated, the autowired AccountRepository in the ConstraintValidator is null even if it isn't null at the same time in the CommandLineRunner that also has the same repository autowired. I don't understand this.
Does anybody have an idea why the repository is null when the validation is triggered from the CommandLineRunner?
Any help is greatly appreciated.
Daniel
Edit: added the CommandLineRunner Code
InitAdminData.java:
import eu.icarus.momca.backend.domain.entity.Account;
import eu.icarus.momca.backend.domain.entity.AccountDetails;
import eu.icarus.momca.backend.domain.entity.AccountProfile;
import eu.icarus.momca.backend.domain.entity.Client;
import eu.icarus.momca.backend.domain.enumeration.AccountRoles;
import eu.icarus.momca.backend.domain.enumeration.ClientRoles;
import eu.icarus.momca.backend.domain.enumeration.GrantTypes;
import eu.icarus.momca.backend.domain.repository.AccountRepository;
import eu.icarus.momca.backend.domain.repository.ClientRepository;
import eu.icarus.momca.backend.domain.service.AccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
#Component
public class InitAdminData implements CommandLineRunner {
private static final Collection<AccountRoles> DEFAULT_ACCOUNT_ROLES = Arrays.asList(AccountRoles.ROLE_ACCOUNT, AccountRoles.ROLE_ADMINISTRATOR, AccountRoles.ROLE_CLIENT_OWNER);
private static final Logger logger = LoggerFactory.getLogger(InitAdminData.class);
private AccountRepository accountRepository;
private AccountService accountService;
#Value("${app.admin-email}")
private String adminEmail;
#Value("${app.admin-name}")
private String adminName;
#Value("${app.admin-password}")
private String adminPassword;
private ClientRepository clientRepository;
#Value("${app.default-client-description}")
private String defaultClientDescription;
#Autowired
public InitAdminData(AccountRepository accountRepository, AccountService accountService, ClientRepository clientRepository) {
this.accountRepository = accountRepository;
this.clientRepository = clientRepository;
this.accountService = accountService;
}
private Account initAdminAccount() {
Account adminAccount = accountRepository.findByAccountProfile_Name(adminName);
if (adminAccount == null) {
adminAccount = new Account();
logger.info("Initialized new admin account");
} else {
logger.info("Refreshed admin account");
}
setAdminAccountData(adminAccount);
return accountService.save(adminAccount, adminPassword);
}
private void initDefaultClient(Account adminAccount) {
long adminAccountId = adminAccount.getId();
Collection<Client> adminClients = clientRepository.findAllByOwnerId(adminAccountId);
if (adminClients.isEmpty()) {
Client defaultClient = new Client();
defaultClient.setOwnerId(adminAccountId);
defaultClient.setDescription(defaultClientDescription);
Collection<ClientRoles> clientRoles = new HashSet<>(2);
clientRoles.add(ClientRoles.ROLE_CLIENT);
clientRoles.add(ClientRoles.ROLE_DEFAULT_CLIENT);
defaultClient.setRoles(clientRoles);
Collection<GrantTypes> grantTypes = new HashSet<>();
grantTypes.add(GrantTypes.authorization_code);
grantTypes.add(GrantTypes.client_credentials);
grantTypes.add(GrantTypes.password);
grantTypes.add(GrantTypes.refresh_token);
defaultClient.setGrantTypes(grantTypes);
defaultClient = clientRepository.save(defaultClient);
logger.info(String.format("Added new default client with id '%s'", defaultClient.getId()));
}
}
#Override
public void run(String... args) {
Account adminAccount = initAdminAccount();
initDefaultClient(adminAccount);
}
private void setAdminAccountData(Account adminAccount) {
adminAccount.setEmail(adminEmail);
adminAccount.setRoles(DEFAULT_ACCOUNT_ROLES);
adminAccount.setAccountDetails(new AccountDetails());
adminAccount.setAccountProfile(new AccountProfile(adminName));
}
}
Add the #ComponentScan annotation.
#ComponentScan
public class InitAdminData implements CommandLineRunner {
...
}
I think you may have few cases :-
you haven't autowired your service and Repo
you haven't scan your Repo where you have your service and Repo.
Or you haven't use #Component(any stereotype) annotation on your classes which you are autowiring

How to inject a typed map of beans based on a typesafe qualifier in Spring?

See the example below, I'm trying to get a Map of my TypedService beans but I would prefer if the keys were the Type enum values specified in the TypeSafeQualifier instead of the unsafe String "serviceName".
package org.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Service;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.Map;
import static org.test.Application.Type.ONE;
import static org.test.Application.Type.TWO;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#SpringBootApplication
public class Application {
#Autowired
Map<String, TypedService> works;
#Autowired
Map<Type, TypedService> fails;
public static void main(String [] args) {
SpringApplication.run(Application.class, args);
}
public enum Type {
ONE,
TWO
}
#Target({TYPE, METHOD, FIELD, CONSTRUCTOR})
#Retention(RUNTIME)
#Qualifier
public #interface TypeSafeQualifier {
Type value();
}
public interface TypedService {
void startSignup();
void activate();
}
#Service
#TypeSafeQualifier(ONE)
public class TypeOneService implements TypedService {
#Override
public void startSignup() {
}
#Override
public void activate() {
}
}
#Service
#TypeSafeQualifier(TWO)
public class TypeTwoService implements TypedService {
#Override
public void startSignup() {
}
#Override
public void activate() {
}
}
}
SpringBoot version: springBootVersion=1.5.3.RELEASE
Spring offers a special approach to handle this type of injection: AutowireCandidateResolver.
In your case the code might be:
package org.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.LinkedHashMap;
import java.util.Map;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#SpringBootApplication
public class Application {
#Autowired
Map<String, TypedService> works;
#Autowired
Map<Type, TypedService> fails;
#PostConstruct
private void init() {
System.out.println(fails);
}
public static void main(String[] args) {
final SpringApplication application = new SpringApplication(Application.class);
application.addInitializers(context -> {
context.addBeanFactoryPostProcessor(beanFactory -> {
final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
dlbf.setAutowireCandidateResolver(new MyAutowireCandidateResolver(dlbf));
});
});
application.run(args);
}
#QualifierValue(TypeSafeQualifier.class)
public enum Type {
ONE,
TWO
}
#Target({TYPE, METHOD, FIELD, CONSTRUCTOR})
#Retention(RUNTIME)
#Qualifier
public #interface TypeSafeQualifier {
Type value();
}
public interface TypedService {
void startSignup();
void activate();
}
#Service
#TypeSafeQualifier(Type.ONE)
public class TypeOneService implements TypedService {
#Override
public void startSignup() {
}
#Override
public void activate() {
}
}
#Target({TYPE})
#Retention(RUNTIME)
public #interface QualifierValue {
Class<? extends Annotation> value();
}
#Service
#TypeSafeQualifier(Type.TWO)
public class TypeTwoService implements TypedService {
#Override
public void startSignup() {
}
#Override
public void activate() {
}
}
private static class MyAutowireCandidateResolver extends ContextAnnotationAutowireCandidateResolver {
private final DefaultListableBeanFactory beanFactory;
private MyAutowireCandidateResolver(DefaultListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
#Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
final Object result = super.getSuggestedValue(descriptor);
if (result != null) {
return result;
}
if (descriptor.getDependencyType() != Map.class) {
return null;
}
final ResolvableType dependencyGenericType = descriptor.getResolvableType().asMap();
final ResolvableType[] typeParams = dependencyGenericType.getGenerics();
final QualifierValue qualifierValue = typeParams[0].getRawClass().getAnnotation(QualifierValue.class);
if (qualifierValue == null) {
return null;
}
final String[] candidateBeanNames = beanFactory.getBeanNamesForType(typeParams[1]);
final LinkedHashMap<Object, Object> injectedMap = new LinkedHashMap<>(candidateBeanNames.length);
for (final String candidateBeanName : candidateBeanNames) {
final Annotation annotation = beanFactory.findAnnotationOnBean(candidateBeanName, qualifierValue.value());
if (annotation == null) {
continue;
}
final Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(annotation, false);
final Object value = annotationAttributes.get("value");
if (value == null || value.getClass() != typeParams[0].getRawClass()) {
continue;
}
injectedMap.put(value, beanFactory.getBean(candidateBeanName));
}
return injectedMap;
}
}
}
First of all, we add TypeQualifierValue annotation to make Spring know about a qualifier with values of the given type.
The second is to customize the SpringApplication in the main method: we use BeanFactoryPostProcessor to set a custom AutowireCandidateResolver.
And the final step: we write MyAutowireCandidateResolver extending ContextAnnotationAutowireCandidateResolver (delegation instead of inheritance is applicable to, it's even a little bit better since one day Spring can migrate to `YetAnotherAutowireCandidateResolver' by default).
The crucial part here is the overridden getSuggestedValue method: here we can customize the injection logic considering the generic types of the dependency (field, method parameter) and by applying some getBean...-like methods from the BeanFactory with some magic of Spring AnnotationUtils class.

Can't integrate Spring Boot Data with Neo4j

I just started with Spring Boot Data Neo4j and trying to finish the movie tutorial. I got this ERROR but not really sure how to debug. Error summary : (1) Error creating bean (2)Could not autowire field. Any help, thank you.
My files structure as follows :
SpringBootApplication.java
package com.test.springdataneothree;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.test.services.MovieService;
#EnableAutoConfiguration
#SpringBootApplication
public class SpringdataneothreeApplication implements CommandLineRunner {
#Autowired
MovieService movieService;
public static void main(String[] args) {
SpringApplication.run(SpringdataneothreeApplication.class, args);
}
#Override
public void run(String... arg0) throws Exception {
System.out.println("Main Spring Boot Class");
movieService.countMovie();
}
}
Movie.java
package com.test.movie;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
#NodeEntity(label ="Movie")
public class Movie {
#GraphId
private Long id;
String title;
int released;
String tagline;
public Movie() { }
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getReleased() {
return released;
}
public void setReleased(int released) {
this.released = released;
}
public String getTagline() {
return tagline;
}
public void setTagline(String tagline) {
this.tagline = tagline;
}
}
MovieService.java
package com.test.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.test.repositories.MovieRepository;
#Service("MovieService")
public class MovieService {
#Autowired
MovieRepository movieRepository;
public void countMovie() {
movieRepository.count();
}
}
MovieRepository.java
package com.test.repositories;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.GraphRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.test.movie.Movie;
#Repository
public interface MovieRepository extends GraphRepository<Movie> {
Movie findById(long id);
Movie findByTitle(#Param("title") String title);
#Query("MATCH (m:Movie) WHERE m.title =~ ('(?i).*'+{title}+'.*') RETURN m")
Collection<Movie> findByTitleContaining(#Param("title") String title);
#Query("MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) RETURN m.title as movie, collect(a.name) as cast LIMIT {limit}")
List<Map<String,Object>> graph(#Param("limit") int limit);
}
MyNeo4jConfiguration.java
package com.test.configuration;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#EnableTransactionManagement
#EnableScheduling
#ComponentScan(basePackages = {"com.test"})
#Configuration
#EnableNeo4jRepositories(basePackages = "com.test.repositories")
public class MyNeo4jConfiguration extends Neo4jConfiguration {
#Override
public SessionFactory getSessionFactory() {
return new SessionFactory("com.test.movie");
}
}
You have a problem with auto wiring: the annotation on the MovieService is wrong, it should be:
#Service("movieService")
or just
#Service
You are trying to inject a bean with the capitalised name, and the context can't find it (the injection is done by name). Another solution is to use the #Qualifier annotation on the auto wired field, like
#Autowired
#Qualifier("MovieService")
MovieService movieService;

Resources