Use ElasticSearch client in non web application with Spring - spring

I'm creating a non web application with Spring that will read data from Kafka and write it in ElasticSearch, i included the following dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
And the following configuration class:
#Configuration
public class ElasticSearchConfig extends AbstractElasticsearchConfiguration{
#Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo("localhost:9200")
.build();
return RestClients.create(clientConfiguration).rest();
}
}
When i execute the following code:
#Override
public void run(String... args) throws Exception {
IndexRequest request = new IndexRequest("test-transactions");
request.id("2");
request.source("{\"name\":\"Sammie\"," + "\"lastname\":\"Goldner\"," + "\"username\":\"hugh.vonrueden\","
+ "\"amount\":9622235.2009}", XContentType.JSON);
client.index(request, RequestOptions.DEFAULT);
}
I get the following exception:
Caused by: java.lang.ClassNotFoundException: org.springframework.http.HttpHeaders
at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_251]
at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[na:1.8.0_251]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) ~[na:1.8.0_251]
at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[na:1.8.0_251]
... 46 common frames omitted
If i include the following dependency the code works fine:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
But it brings a lot of other functionalities that i don't want in my code, is there any other way to configure spring-boot-starter-data-elasticsearch in a non web application?
Thanks.

Related

How to configure Oracle XA Datasource on spring boot app that communicating with an EJB in JBoss EAP

I'm trying to use an EJB on a Spring boot application. This application runs in 'undertow' and it can call an EJB on a JBoss EAP (Jboss EAP 7.0).
I tried to configure the application to use Oracle XA transactions without success.
What I need is that when the Spring boot app calls the EJB and, after the call, some failure occurs on this app that EJB does a rollback the previous work.
Follows some configurations of the spring boot app:
pom
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-ejb-client-bom</artifactId>
<version>17.0.0.Final</version>
<type>pom</type>
</dependency>
</dependencies>
properties
spring.datasource.url=jdbc:oracle:thin:#<host>:<port>:<data>
spring.datasource.username=user
spring.datasource.password=pass
spring.datasource.xa.data-source-class-name=bitronix.tm.resource.jdbc.lrc.LrcXADataSource
spring.datasource.xa.properties.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.jpa.properties.hibernate.hbm2ddl.schema_filter_provider=<class>
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.integration.envers.enabled=false
App Class
Configuration
#SpringBootApplication
#EnableTransactionManagement
#ComponentScan(basePackages={...,...})
#EntityScan(basePackages={...,...,...})
public class SpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplication .class, args);
}
EJB 'producer'
#Configuration
public class EJBConfiguration {
#Bean
public Context context() throws NamingException {
Properties jndiProps = new Properties();
jndiProps.put("java.naming.factory.initial", "org.jboss.naming.remote.client.InitialContextFactory");
jndiProps.put("jboss.naming.client.ejb.context", true);
jndiProps.put("java.naming.provider.url", "http-remoting://localhost:8081");
return new InitialContext(jndiProps);
}
#Bean
public EJBRemote(Context c) throws NamingException {
return (EJBRemote) c.lookup("full EJB remote name")
}
}
the call of EJB is made as follows:
stuff
Spring boot App controller
class AppControler {
#AutoWired
Service service
#PostMapping("/")
#Transactional
public ResponseEntity controllerMethod() {
return new ResponseEntity<>(this.service.doStuff(), HttpStatus.OK);
}
}
Spring boot app service
class Service {
#Autowired
private EJBRemote EJBRemote;
#Autowired
private DAOObject dao;
#TransactionAttribute(TransactionAttributeType.MANDATORY)
Public Object doStuff() {
dao.save();
EJBRemote.saveSomeThing(); // OK operation, needs to rolback if oerros occurs after
dao.saveOtherThing();// errors occurs
}
}
the EJB implementation class on JBoss EAP is like
class EJBRemoteImpl implements EJBRemote {
#Override
public void saveSomeThing() {
someDao.save();
}
}
So when a error occurs in 'dao.saveOtherThing();' the operation made in 'EJBRemove.saveSomeThing();' needs to rolback.
If I'm put '#TransactionAttribute(TransactionAttributeType.MANDATORY)' on EJB this error occurs:
javax.ejb.EJBTransactionRequiredException: WFLYEJB0062: A transaction is required to call org.jboss.invocation.InterceptorContext#493c9dbb
at org.jboss.as.ejb3.tx.CMTTxInterceptor.mandatory(CMTTxInterceptor.java:289) ~[na:na]
at org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:233) ~[na:na]
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) ~[na:na]
at org.jboss.as.ejb3.remote.EJBRemoteTransactionPropagatingInterceptor.processInvocation(EJBRemoteTransactionPropagatingInterceptor.java:79) ~[na:na]
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) ~[na:na]
at org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41) ~[na:na]
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) ~[na:na]
at org.jboss.as.ejb3.component.invocationmetrics.WaitTimeInterceptor.processInvocation(WaitTimeInterceptor.java:43) ~[na:na]
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) ~[na:na]
.
.
.
If the annotation (#TransactionAttribute(TransactionAttributeType.MANDATORY)) is not present the calls to EJB occurs normally but the rollback not occurs.
You are trying create an EJB transaction in a Spring Service, if you want do it you need convert your service to an EJB, so automatically all operations will be TransactionAttribute.REQUIRED, if you want that this method work with a Spring transaction, you need to use #Transactional(propagation = Propagation.MANDATORY) from Spring (org.springframework.transaction.annotation.Transactional), but I don't know if an Spring transaction works in distributed context, but you can try. If not converting the Bean to EJB will work.

Spring Boot test tries to initialize cache2k for the 2nd time and fails

After adding cache2k to my project some #SpringBootTest's stopped working with an error:
java.lang.IllegalStateException: Cache already created: 'cache'
Below I provide the minimal example to reproduce:
Go to start.spring.io and create a simplest Maven project with Cache starter, then add cache2k dependencies:
<properties>
<java.version>1.8</java.version>
<cache2k-version>1.2.2.Final</cache2k-version>
</properties>
<dependencies>
<dependency>
<groupId>org.cache2k</groupId>
<artifactId>cache2k-api</artifactId>
<version>${cache2k-version}</version>
</dependency>
<dependency>
<groupId>org.cache2k</groupId>
<artifactId>cache2k-core</artifactId>
<version>${cache2k-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.cache2k</groupId>
<artifactId>cache2k-spring</artifactId>
<version>${cache2k-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Now configure the simplest cache:
#SpringBootApplication
#EnableCaching
public class CachingDemoApplication {
public static void main(String[] args) {
SpringApplication.run(CachingDemoApplication.class, args);
}
#Bean
public CacheManager springCacheManager() {
SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager();
cacheManager.addCaches(b -> b.name("cache"));
return cacheManager;
}
}
And add any service (which we will #MockBean in one of our tests:
#Service
public class SomeService {
public String getString() {
System.out.println("Executing service method");
return "foo";
}
}
Now two #SpringBootTest tests are required to reproduce the issue:
#SpringBootTest
#RunWith(SpringRunner.class)
public class SpringBootAppTest {
#Test
public void getString() {
System.out.println("Empty test");
}
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class WithMockedBeanTest {
#MockBean
SomeService service;
#Test
public void contextLoads() {
}
}
Notice that the 2nd test has mocked #MockBean. This causes an error (stacktrace below).
Caused by: java.lang.IllegalStateException: Cache already created: 'cache'
at org.cache2k.core.CacheManagerImpl.newCache(CacheManagerImpl.java:174)
at org.cache2k.core.InternalCache2kBuilder.buildAsIs(InternalCache2kBuilder.java:239)
at org.cache2k.core.InternalCache2kBuilder.build(InternalCache2kBuilder.java:182)
at org.cache2k.core.Cache2kCoreProviderImpl.createCache(Cache2kCoreProviderImpl.java:215)
at org.cache2k.Cache2kBuilder.build(Cache2kBuilder.java:837)
at org.cache2k.extra.spring.SpringCache2kCacheManager.buildAndWrap(SpringCache2kCacheManager.java:205)
at org.cache2k.extra.spring.SpringCache2kCacheManager.lambda$addCache$2(SpringCache2kCacheManager.java:143)
at java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1853)
at org.cache2k.extra.spring.SpringCache2kCacheManager.addCache(SpringCache2kCacheManager.java:141)
at org.cache2k.extra.spring.SpringCache2kCacheManager.addCaches(SpringCache2kCacheManager.java:132)
at com.example.cachingdemo.CachingDemoApplication.springCacheManager(CachingDemoApplication.java:23)
at com.example.cachingdemo.CachingDemoApplication$$EnhancerBySpringCGLIB$$2dce99ca.CGLIB$springCacheManager$0(<generated>)
at com.example.cachingdemo.CachingDemoApplication$$EnhancerBySpringCGLIB$$2dce99ca$$FastClassBySpringCGLIB$$bbd240c0.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363)
at com.example.cachingdemo.CachingDemoApplication$$EnhancerBySpringCGLIB$$2dce99ca.springCacheManager(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 52 more
If you remove #MockBean, both tests will pass.
How can I avoid this error in my test suite?
Your second test represents a different ApplicationContext altogether so the test framework will initiate a dedicated one for it. If cache2k is stateful (for instance sharing the CacheManager for a given classloader if it already exists), the second context will attempt to create a new CacheManager while the first one is still active.
You either need to flag one of the test as dirty (see #DirtiesContext) which will close the context and shut down the CacheManager, or you can replace the cache infrastructure by an option that does not require all that, see #AutoConfigureCache.
If cache2k works in such a way that it requires you to dirty the context, I'd highly recommend to swap it using the later options.
Since I do not want any custom behavior in test, but just want to get rid of this error, the solution is to create CacheManager using unique name like this:
#Bean
public CacheManager springCacheManager() {
SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager("spring-" + hashCode());
cacheManager.addCaches(b -> b.name("cache"));
return cacheManager;
}
I encountered the same error when using cache2k with Spring Dev Tools, and ended up with the following code as the solution:
#Bean
public CacheManager cacheManager() {
SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager();
// To avoid the "Caused by: java.lang.IllegalStateException: Cache already created:"
// error when Spring DevTools is enabled and code reloaded
if (cacheManager.getCacheNames().stream()
.filter(name -> name.equals("cache"))
.count() == 0) {
cacheManager.addCaches(
b -> b.name("cache")
);
}
return cacheManager;
}

Problems setting up Spring form validation

I have some problems setting up the validation in Spring MVC (4.3.9) application.
Basically, there is a simple application, with the issue isolated to one "contact" form.
Here is part of the code of the bean underlying the form:
#Component
#Scope("request")
public class SendMail {
#Min(20)
private String userName;
#Size(min=10, max=11)
private String message;
private String userEmail;
public static class CustomValidator implements Validator {
#Override
public boolean supports(Class clazz) {
return SendMail.class.equals(clazz);
}
#Override
public void validate(Object targets, Errors errors) {
SendMail o = (SendMail) targets;
if (o.getUserName() == null || o.getUserName().length() < 1) {
errors.rejectValue("userName", "Empty.userName");
}
}
}
....
}
and here is the part of the relevant controller:
#RequestMapping(value = {"/contact"}, method = RequestMethod.POST)
public ModelAndView sendEmail(#Valid SendMail sendmail, BindingResult result) {
System.out.println("Result1: " + result.hasErrors());
DataBinder binder = new DataBinder(sendmail);
binder.setValidator(new SendMail.CustomValidator());
binder.validate();
result = binder.getBindingResult();
System.out.println("Result2: " + result.hasErrors());
I get the following result:
Result1: false
Result2: true
So, the 'general' (denoted by the #Valid annotation) validation doesnt seem to work, but a manually-invoked validation does.
I googled for some time and have come across suggestions that a validation bean needs to be defined in the spring config. Unfortunately, when I add the following to my Config.java:
....
#Bean(name = "validator")
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
return validator;
}
The application crashes with the following exception:
....
root cause
java.lang.ClassNotFoundException: javax.el.ExpressionFactory
java.net.URLClassLoader.findClass(URLClassLoader.java:381)
java.lang.ClassLoader.loadClass(ClassLoader.java:424)
sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
java.lang.ClassLoader.loadClass(ClassLoader.java:357)
java.lang.ClassLoader.defineClass1(Native Method)
java.lang.ClassLoader.defineClass(ClassLoader.java:763)
java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
java.net.URLClassLoader.access$100(URLClassLoader.java:73)
java.net.URLClassLoader$1.run(URLClassLoader.java:368)
java.net.URLClassLoader$1.run(URLClassLoader.java:362)
java.security.AccessController.doPrivileged(Native Method)
java.net.URLClassLoader.findClass(URLClassLoader.java:361)
java.lang.ClassLoader.loadClass(ClassLoader.java:424)
sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
java.lang.ClassLoader.loadClass(ClassLoader.java:411)
java.lang.ClassLoader.loadClass(ClassLoader.java:357)
java.lang.Class.forName0(Native Method)
java.lang.Class.forName(Class.java:348)
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(Unknown Source)
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(Unknown Source)
javax.el.ExpressionFactory.newInstance(Unknown Source)
javax.el.ExpressionFactory.newInstance(Unknown Source)
....
This supposedly indicates, there is no lib with EL implementation. And here is where I'm stuck. I have tried a number of dependency (using maven) with no avail. Currently the relevant part of my pom looks like this:
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>5.4.1.Final</version>
</dependency>
I have tried various set-ups, with el-impl 2.2, etc. addin el.jar to Tomcat clasppath. But no luck at all =(
What is even more starange, if you look at the exception above, the call actually goes through "javax.el.ExpressionFactory.newInstance(Unknown Source)" before resulting in "java.lang.ClassNotFoundException: javax.el.ExpressionFactory".
I'd be grateful for any hints on how to fix/debug the issue...
Add this dependency .
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
</dependency>
Follow solution here .
I have found the solution to my issue. Strange as it seems, when I added el.jar supplied with Tomcat to the classpath in catalina.sh, the validation works. It might be a distro-related issue (i'm running it on Gentoo).

Spring Integration Java DSL problems at startup with Http Outbound Gateway

I'm new to Spring Integration and am trying to develop an application which simply (1) polls a folder for new files with extension .dat containing data in csv format (2) initialize for each line a domain POJO object of class RecordDTO and finally (3) sends this object as payload to a REST service with a POST.
For this I'm trying to use Java DSL for Spring Integration.
The problem I'm getting is the following VerifyError / HttpRequestExecutingMessageHandler overrides final method onInit. exception.
2016-07-22 10:01:38.965 ERROR 4460 --- [ main] o.s.boot.SpringApplication : Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'upcase' defined in eu.softeco.examples.SpringBootIntegrationTestApplication: Initialization of bean failed; nest
ed exception is java.lang.VerifyError: class org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler overrides final method onInit.()V
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) ~[spring-beans-4.3.1.RELEASE.jar:4.3.1.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.3.1.RELEASE.jar:4.3.1.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.1.RELEASE.jar:4.3.1.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.1.RELEASE.jar:4.3.1.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.1.RELEASE.jar:4.3.1.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.1.RELEASE.jar:4.3.1.RELEASE]
...
Caused by: java.lang.VerifyError: class org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler overrides final method onInit.()V
at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_45]
at java.lang.ClassLoader.defineClass(ClassLoader.java:760) ~[na:1.8.0_45]
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[na:1.8.0_45]
...
Below are relevant details concerning my code and maven configuration. Please not that
1) the problem is at startup, before any data is put in the input folder
2) if I replace the final Http outboundGateway step with the simple (commented) System.out.println, everything works fine.
Following are code/config details. This is my main Spring Boot application class with Spring Integration Flow definition:
#SpringBootApplication
#EnableIntegration
#IntegrationComponentScan
public class SpringBootIntegrationTestApplication {
...
public static void main(String[] args) {
SpringApplication.run(SpringBootIntegrationTestApplication.class, args);
}
/**
* Inbound FileReadingMessageSource
*/
#Bean
#InboundChannelAdapter(channel = "upcase.input", poller = #Poller(fixedDelay = "4000"))
public MessageSource<File> fileReadingMessageSource() {
FileReadingMessageSource source = new FileReadingMessageSource();
source.setDirectory(new File(INBOUND_PATH));
source.setFilter(new SimplePatternFileListFilter("*.dat"));
return source;
}
/**
* Spring Integration Java DSL Flow
*/
#Bean
public IntegrationFlow upcase() {
FileToStringTransformer fileToStringTranformer = Transformers.fileToString();
fileToStringTranformer.setDeleteFiles(true);
return flow -> flow
// get string contents from fie
.transform(fileToStringTranformer)
// split into individual lines
.split( s -> s.applySequence(true).get().getT2().setDelimiters("\n"))
// cleanup lines from trailing returns
.transform( (String s) -> s.replace("\n", "").replace("\r", "") )
// convert csv string to RecordDTO object
.transform("#recordFromCsvTransformer.transform(payload)")
// print on System.out
// .handle(m -> System.out.println(m.getPayload()))
// send to
.handle(Http.outboundGateway("http://localhost:8080/records")
.httpMethod(HttpMethod.POST)
.expectedResponseType(RecordDTO.class));
}
}
Below the RecordFromCsvTransformer class
#Component
public class RecordFromCsvTransformer
{
#Transformer
public RecordDTO transform(String csvline) throws Exception {
RecordDTO record = new RecordDTO();
... parse csv and initialize record's fields...
return record;
}
}
And Finally the relevant parts of pom.xml (dependencies);
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RC1</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-file</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-http</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
...
</dependencies>
...
As a side question in general, can someone suggest me some good tutorial / getting started guide to learn Spring Integration with Java Annotation/Java DSL? So far I've only found either introductory guide based on Spring Integration XML configuration or material about Java Annotations/DSL but that already requires prior knowledge of Spring Integration.
<version>2.1.0.RELEASE</version>
That version of Spring Integration is mismatched with the spring-integration-core version brought in transitively by maven.
You need to use the same version of all spring-integration-* files - check which version of spring-integration-file is being pulled in by boot and use the same version.

Facebook instance not getting created in Spring Social

I am trying to run a simple Spring Social application to login to Facebook account. But, Facebook instance is not created and it is showing nullpointerexception.
#Controller
#RequestMapping("/fb")
public class HelloFBController {
#Autowired
private Facebook facebook;
#RequestMapping(method = RequestMethod.GET)
public String helloFacebook(Model model) {
if (!facebook.isAuthorized()) {
return "redirect:/connect/facebook";
}
model.addAttribute("feed", facebook.feedOperations().getFeed());
return "hellofb";
}
}
Then
#Configuration
public class SocialConfig {
#Bean
public ConnectionFactoryLocator connectionFactoryLocator() {
ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();
registry.addConnectionFactory(new FacebookConnectionFactory(
environment.getProperty("facebook.appId"),
environment.getProperty("facebook.appSecret")));
return registry;
}
#Inject
private Environment environment;
}
Then application.properties file
spring.social.facebook.appId=XXXXXXXXXXXX
spring.social.facebook.appSecret=XXXXXXXXXXXXXXXXXXX
Then pom.xml is
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-social-facebook</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
Whether I am missing something. When I run this application using Spring Boot, I am getting the exception in Controller as bellow. I am trying to access the application at localhost:8080/fb
java.lang.NullPointerException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at $Proxy42.isAuthorized(Unknown Source)
at net.javabeat.spring.social.web.HelloFBController.helloFacebook(HelloFBController.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvok
You are missing the facebook dependency, see below:
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-facebook</artifactId>
</dependency>
Follow link below for more information:
Accessing Facebook Data
Do you have this project in a Git repository somewhere that I could try without having to recreate the example from the blog entry you pulled it from?
I also notice that your Maven dependencies are reference Spring Boot starters for Spring Social Facebook, but that you are explicitly configuring the connection factory locator (and nothing else). Consequently, if you are using Spring Boot, then your explicit configuration is probably disabled auto-configuration provided by Spring Boot. Delete SocialConfig and see what happens.
You can change the code for a workaround to make it working. At this time facebook is not connnected or injected so it is getting NPE:
try {
if (!facebook.isAuthorized()) {
return "redirect:/connect/facebook";
}
} catch (Exception e) {
return "redirect:/connect/facebook";
}
Also, you may have to add spring.social.auto_connection_views=true if you are not using custom view.

Resources