I have a Spring-boot app.
I want to use variable from application.properties in class method but I have nullPointerException.
Here's a simple example that doesn't work.
application.properties:
#data paths
file.path=C:\\Users\\apodar\\autoTest
Config.java
package com.eserv.autotest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class Config {
#Value("${file.path}")
String filePath;
public String getFilePath() { return filePath; }
public String getScreenshotsPath() {
return getFilePath() + "/screenshots";
}
}
AutotestApplication.java
package com.eserv.autotest;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.Transactional;
#SpringBootApplication(
scanBasePackageClasses = {
AutotestApplication.class,
}
)
public class AutotestApplication implements CommandLineRunner {
#Autowired DataSource dataSource;
public static void main(String[] args) {
SpringApplication.run(AutotestApplication.class, args);
}
#Transactional(readOnly = true)
#Override
public void run(String... args) throws Exception {
System.out.println("DATASOURCE = " + dataSource);
}
}
SeleniumTestExecutionListener:
public class SeleniumTestExecutionListener extends AbstractTestExecutionListener {
#Inject Config config;
private WebDriver webDriver;
#Override
public void afterTestMethod(TestContext testContext) throws Exception {
if (testContext.getTestException() == null) {
return;
}
File screenshot = ((TakesScreenshot) webDriver).getScreenshotAs(OutputType.FILE);
String testName = toLowerUnderscore(testContext.getTestClass().getSimpleName());
String methodName = toLowerUnderscore(testContext.getTestMethod().getName());
FileUtils.copyFile(screenshot, new File( config.getScreenshotsPath() + testName + "_" + methodName + "_" + screenshot.getName()));
}
}
Why does config.getScreenshotsPath() method doesn't return path. config is null.
Autowiring in a TestExecutionListener will not work. The creation and lifecycle of the TestExecutionListener instances is managed by the Test Context framework of Spring and that isn't part of the ApplicationContext but external. Hence auto wiring will not work.
If you want to use beans in your TestExecutionListener instead retrieve the ApplicationContext from the TestContext.
#Override
public void afterTestMethod(TestContext testContext) throws Exception {
if (testContext.getTestException() == null) {
return;
}
final Config config = testContext.getApplicationContext().getBean(Config.class);
File screenshot = ((TakesScreenshot) webDriver).getScreenshotAs(OutputType.FILE);
String testName = toLowerUnderscore(testContext.getTestClass().getSimpleName());
String methodName = toLowerUnderscore(testContext.getTestMethod().getName());
FileUtils.copyFile(screenshot, new File( config.getScreenshotsPath() + testName + "_" + methodName + "_" + screenshot.getName()));
}
Related
I created a simple aspect in Spring using Spring Boot 2.1.6.RELEASE.
It basically logs the total time spent on a method.
#Aspect
#Component
public class TimeLoggerAspect {
private static final Logger log = LoggerFactory.getLogger(TimeLoggerAspect.class);
#Around("#annotation(demo.TimeLogger)")
public Object methodTimeLogger(ProceedingJoinPoint joinPoint)
throws Throwable {
long startTime = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long totalTime = System.currentTimeMillis() - startTime;
log.info("Method " + joinPoint.getSignature() + ": " + totalTime + "ms");
return proceed;
}
}
the aspect is triggered by a TimeLogger annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface TimeLogger {
}
and is used in a component like this
#Component
public class DemoComponent {
#TimeLogger
public void sayHello() {
System.out.println("hello");
}
}
A spring boot demo application will invoke sayHello via the run method of the CommandLineRunner interface.
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
#Autowired
private DemoComponent demoComponent;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
demoComponent.sayHello();
}
}
For completeness, I add my modifications in build.gradle: adding libraries for aop, spring test and jupiter (junit).
compile("org.springframework.boot:spring-boot-starter-aop")
testCompile("org.springframework.boot:spring-boot-starter-test")
testCompile("org.junit.jupiter:junit-jupiter-api")
testRuntime("org.junit.jupiter:junit-jupiter-engine")
Running the application will output (trimmed for readability)
hello
... TimeLoggerAspect : Method void demo.DemoComponent.sayHello(): 4ms
So far, so good. Now I create a test based on #SpringBootTest annotation and jupiter.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = {DemoComponent.class, TimeLoggerAspect.class})
public class DemoComponentFailTest {
#Autowired
private DemoComponent demoComponent;
#Test
public void shouldLogMethodTiming() {
demoComponent.sayHello();
}
}
and here I get the output
hello
No output from the TimeLoggerAspect, since it seems it is not being triggered.
Is something missing to trigger the aspect in the test? Or are there other ways of testing the aspect in spring boot?
I had similar problem. My Aspect is listening on controller methods. To get it activated, importing the AnnotationAwareAspectJAutoProxyCreator made the trick:
#RunWith(SpringRunner.class)
#Import(AnnotationAwareAspectJAutoProxyCreator.class) // activate aspect
#WebMvcTest(MyController.class)
public class MyControllerTest {
...
}
You have to put #EnableAspectJAutoProxy with your file #Configuration that declares the bean with #Aspect.
#Aspect
#Configuration
#EnableAspectJAutoProxy
public class TimeLoggerAspect {
private static final Logger log = LoggerFactory.getLogger(TimeLoggerAspect.class);
#Around("#annotation(demo.TimeLogger)")
public Object methodTimeLogger(ProceedingJoinPoint joinPoint)
throws Throwable {
long startTime = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long totalTime = System.currentTimeMillis() - startTime;
log.info("Method " + joinPoint.getSignature() + ": " + totalTime + "ms");
return proceed;
}
}
I think that will do the work.
You need to start an #SpringBootApplication. However, it does not have to be the one you use to start your app in production. It can be a special one for this test only and can be in your test sources root not your src.
#SpringBootApplication
#ComponentScan(basePackageClasses = {DemoComponent.class, TimeLoggerAspect.class})
public class SpringBootTestMain {
public static void main(String[] args) {
SpringApplication.run(SpringBootTestMain.class, args);
}
}
Then in your test this is the only class you need to list.
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = SpringBootTestMain.class)
public class DemoComponentFailTest {
Another solution that seems to work is adding AnnotationAwareAspectJAutoProxyCreator in classes of #SpringBootTest, although I am not quite certain why.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = { DemoComponent.class,
TimeLoggerAspect.class,
AnnotationAwareAspectJAutoProxyCreator.class })
public class DemoComponentFailTest {
#Autowired
private DemoComponent demoComponent;
#Test
public void shouldLogMethodTiming() {
demoComponent.sayHello();
}
}
When I had to test an aspect, I used the approach below.
#SpringBootTest
#ContextConfiguration(classes = {MyAspectImpl.class, MyAspectTest.TestConfiguration.class})
#EnableAspectJAutoProxy
public class MyAspectTest {
#org.springframework.boot.test.context.TestConfiguration
static class TestConfiguration {
#Bean
public MyAspectTestClass myAspectTestClass() {
return new MyAspectTestClass();
}
}
#Autowired
private MyAspectTestClass target;
#Test
public void testCorrectlySetsPoolNameUsingMethodParameter() {
target.testMethod();
}
#NoArgsConstructor
private static class MyAspectTestClass {
#MyAspect
public void testMethod() {
//Add some logic here
}
}
}
I'm the one leaning how to write a code using Spring Boot. Then when I tried to write a code that used abstract class, I got an error as below.
Description:
Parameter 0 of constructor in com.in28minutes.spring.practice.springmasterclasspractice.devicefactory.LaptopManufacturingProcess required a bean of type 'java.lang.String' that could not be found.
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.
Could you guys give me an advise how I could solve the error?
Spring Boot: v2.1.4
Java: 10.0.2
Maven: 3.6.0
SpringMasterClassPracticeDeviceFactoryApplication class
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
#SpringBootApplication
public class SpringMasterClassPracticeDeviceFactoryApplication {
private static Logger LOGGER = LoggerFactory.getLogger(SpringMasterClassPracticeDeviceFactoryApplication.class);
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication
.run(SpringMasterClassPracticeDeviceFactoryApplication.class, args);
ManufacturingImpl manufacturingImpl = applicationContext.getBean(ManufacturingImpl.class);
System.out.println(manufacturingImpl);
// manufacturingImpl.manifactureProduct("Laptop Process");
LOGGER.info("{}", manufacturingImpl);
}
}
ManufacturingImpl class
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
#Component
public class ManufacturingImpl {
#Autowired
#Qualifier("laptop")
private GeneralManufacturingProcess generalManufacturingProcess;
public void manifactureProduct(String processName) {
System.out.println(generalManufacturingProcess);
generalManufacturingProcess.launchProcess();
}
}
GeneralManufacturingProcess class
public abstract class GeneralManufacturingProcess {
private String processName;
public GeneralManufacturingProcess(String processName) {
this.processName = processName;
}
public String getProcessName() {
return processName;
}
public void launchProcess() {
if (processName != null && !processName.isEmpty()) {
assembleDevice();
testDevice();
packageDevice();
storeDevice();
} else {
System.out.println("No process name was specified");
}
}
protected abstract void assembleDevice();
protected abstract void testDevice();
protected abstract void packageDevice();
protected abstract void storeDevice();
}
LaptopManufacturingProcess class
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
#Component
#Qualifier("laptop")
public class LaptopManufacturingProcess extends GeneralManufacturingProcess {
public LaptopManufacturingProcess(String processName) {
super(processName);
}
#Override
protected void assembleDevice() {
System.out.println("Assembled laptop: " + getProcessName());
}
#Override
protected void testDevice() {
System.out.println("Tested laptop: " + getProcessName());
}
#Override
protected void packageDevice() {
System.out.println("Packaged laptop: " + getProcessName());
}
#Override
protected void storeDevice() {
System.out.println("Stored laptop: " + getProcessName());
}
}
There are Multiple ways to solve this. The problem is, that the Spring Framework is trying to create an instance of LaptopManufacturingProcess with the single constructor, which accepts a String. So the Framework is trying to autowire a Bean of type String into the constructor, which simply does not work.
Basically, what you can do is the following:
create a no-args constructor, and have it pass a hardcoded string to the parent constructor:
public LaptopManufacturingProcess() {
super("String");
}
Add an #Value-Annotation to read the String from a PropertySource:
public LaptopManufacturingProcess(#Value("${property.key.here}") String processName) {
super(processName);
}
Create a Factory Bean to create instances of GeneralManufacturingProcess on demand
I've got a simple java web service sample program from the internet:
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
import java.util.Date;
#WebService
interface IService {
void hello(#WebParam(name="username") String username);
}
#WebService(targetNamespace = "ServiceImpl", endpointInterface="IService")
class ServiceImp implements IService{
#Override
public void hello(#WebParam(name = "username") String username) {
System.out.println("hello " + username + " now is " + new Date());
}
}
public class ServiceMain {
public static void main(String[] args) {
String address = "http://localhost:7777/myService";
Endpoint.publish(address, new ServiceImp());
System.out.println("OK");
}
}
It compiles and runs with exception:
Exception in thread "main" java.lang.NullPointerException
at com.sun.xml.internal.ws.model.RuntimeModeler.getPortTypeName(RuntimeModeler.java:1618)
at com.sun.xml.internal.ws.model.RuntimeModeler.getPortTypeName(RuntimeModeler.java:1584)
at com.sun.xml.internal.ws.server.EndpointFactory.create(EndpointFactory.java:226)
at com.sun.xml.internal.ws.server.EndpointFactory.createEndpoint(EndpointFactory.java:144)
at com.sun.xml.internal.ws.api.server.WSEndpoint.create(WSEndpoint.java:563)
at com.sun.xml.internal.ws.api.server.WSEndpoint.create(WSEndpoint.java:545)
at com.sun.xml.internal.ws.transport.http.server.EndpointImpl.createEndpoint(EndpointImpl.java:308)
at com.sun.xml.internal.ws.transport.http.server.EndpointImpl.publish(EndpointImpl.java:231)
at com.sun.xml.internal.ws.spi.ProviderImpl.createAndPublishEndpoint(ProviderImpl.java:126)
at javax.xml.ws.Endpoint.publish(Endpoint.java:240)
at ServiceMain.main(ServiceMain.java:22)
So where does this code snippet get wrong, and how to fix it?
Thanks a lot.
You have to provide fully qualified name for endpoint interface. Try this if you wanna lose the endpoint interface.
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
import java.util.Date;
interface IService {
void hello(String username);
}
#WebService(targetNamespace = "ServiceImpl")
class ServiceImp implements IService{
public void hello(#WebParam(name = "username") String username) {
System.out.println("hello " + username + " now is " + new Date());
}
}
public class ServiceMain {
public static void main(String[] args) {
String address = "http://localhost:7777/myService";
Endpoint.publish(address, new ServiceImp());
System.out.println("OK");
}
}
Otherwise, assuming your endpoint interface resides in a package called your.pkg, try this.
package your.pkg;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
import java.util.Date;
#WebService
interface IService {
void hello(String username);
}
#WebService(targetNamespace = "ServiceImpl", endpointInterface="your.pkg.IService")
class ServiceImp implements IService{
public void hello(#WebParam(name = "username") String username) {
System.out.println("hello " + username + " now is " + new Date());
}
}
public class ServiceMain {
public static void main(String[] args) {
String address = "http://localhost:7777/myService";
Endpoint.publish(address, new ServiceImp());
System.out.println("OK");
}
}
I was able to run it with both approaches and started getting WSDL from endpoint :- http://localhost:7777/myService?wsdl
This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 5 years ago.
I am not able to #autowire a class in spring boot application. below is the project explorer snapshot:
From my main class CrmDisconnectionApplication, I am calling DisconnectionConTrigger class. In that class I am doing #autowire for YamlConfig. But I am getting null pointer exception.
below is the code:
CrmDisconnectionApplication
package com.wpits.crm.disconnection;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.wpits.crm.disconnection.quartzJob.DisconnectionCronTrigger;
#SpringBootApplication(scanBasePackages = { "com.wpits.crm" })
public class CrmDisconnectionApplication {
public static void main(String[] args) {
SpringApplication.run(CrmDisconnectionApplication.class, args);
DisconnectionCronTrigger disconnectionCronTrigger = DisconnectionCronTrigger.getInstance();
disconnectionCronTrigger.initialize();
}
}
DisconnectionCronTrigger
package com.wpits.crm.disconnection.quartzJob;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.wpits.crm.disconnection.config.YamlConfig;
#Component
public class DisconnectionCronTrigger {
#Autowired
private YamlConfig myConfig;
private static DisconnectionCronTrigger obj = null;
private DisconnectionCronTrigger() {}
public static DisconnectionCronTrigger getInstance() {
if(obj == null) {
obj = new DisconnectionCronTrigger();
}
return obj;
}
public void initialize() {
System.out.println("using environment: " + myConfig.getEnvironment());
System.out.println("name: " + myConfig.getName());
System.out.println("servers: " + myConfig.getServers());
System.out.println("hobies: "+myConfig.getHobies());
JobDetail job = JobBuilder.newJob(DisconnectionJob.class).withIdentity("DisconnectionJob", "group1").build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("cronTrigger", "group1").withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")).build();
try {
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
}catch(Exception ex) {
ex.printStackTrace();
}
}
}
YamlConfig
package com.wpits.crm.disconnection.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.*;
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties
public class YamlConfig {
private String name;
private String environment;
private List<String> servers = new ArrayList<>();
private List<String> hobies = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEnvironment() {
return environment;
}
public void setEnvironment(String environment) {
this.environment = environment;
}
public List<String> getServers() {
return servers;
}
public void setServers(List<String> servers) {
this.servers = servers;
}
public List<String> getHobies() {
return hobies;
}
public void setHobies(List<String> hobies) {
this.hobies = hobies;
}
}
I am getting null pointer exception for line System.out.println("using environment: " + myConfig.getEnvironment()); in class DisconnectionCronTrigger. Where am I getting it wrong. Please correct me..
The problem is this line
DisconnectionCronTrigger disconnectionCronTrigger = DisconnectionCronTrigger.getInstance();
In getInstance you are creating a new object using new. You should not do new, instead Autowire the bean or get it from Spring application context.
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(CrmDisconnectionApplication.class, args);
DisconnectionCronTrigger disconnectionCronTrigger = (DisconnectionCronTrigger)context.getBean("disconnectionCronTrigger");
disconnectionCronTrigger.initialize();
}
If you do it like this, then you will get an object will all the fields in the bean autowired. If you create a object using new, then you wont.
I am trying to get a reference to the ResourceResolver from the ResourceResolverFactory as follows:
#Reference
private ResourceResolverFactory resourceResolverFactory;
public void someMethod() {
Map<String, Object> authenticationMap = new HashMap<String, Object>();
authenticationMap.put(ResourceResolverFactory.USER, "user");
authenticationMap.put(ResourceResolverFactory.PASSWORD, "pwd");
//This line returns NullPointerException
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(authenticationMap);
}
Can someone please tell me what I am doing wrong? The AEM API version v6.0.
So what I did was to create an Activator class that is called when the bundle is deployed and started. The Activator class then gets the instance of org.apache.sling.jcr.api.SlingRepository which we can use to connect to the JCR. Here is the activator code:
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.jcr.api.SlingRepository;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Component(immediate = true, label = "Commons Activator")
public class Activator implements BundleActivator {
#Reference
private SlingRepository repository;
private static final Logger logger = LoggerFactory.getLogger(Activator.class);
#Activate
#Override
public void start(BundleContext context) throws Exception {
logger.info(context.getBundle().getSymbolicName() + " started");
//My own factory class instance
ResourceResolverDiscoveryService rrf = ResourceResolverDiscoveryService.getInstance();
//Set the 'repository' in your factory class instance
rrf.setSlingRepositoryFactory(repository);
}
#Deactivate
#Override
public void stop(BundleContext context) throws Exception {
logger.info(context.getBundle().getSymbolicName() + " stopped");
}
}
Then in the class where I want to use JCR to store the data I did the following:
public class StoreInJCR {
public void store(Quote quote) throws LoginException, RepositoryException {
SlingRepository slingRepository = ResourceResolverDiscoveryService.getInstance().getSlingRepositoryFactory();
// GOT IT!!! Mission Accomplished
Session session = slingRepository.loginAdministrative(null);
Node root = session.getRootNode();
// Further code
.
.
}
}
Hope someone finds this useful.