Following the Spring Boot reference guides, I've set up a Hello World example. My workplace uses Ant, so I implemented build.xml based on https://www.mkyong.com/ant/ant-spring-mvc-and-war-file-example/ The resultant WAR file works correctly when deployed onto the WebLogic 12c server. Note: As per this guide, .properties files are copied to ${web.classes.dir}.
Now, I want to query the server's Oracle SQL database via JNDI. Following various parts of the Spring Boot references & guides, this is my modified code at present:
#SpringBootApplication
public class Application extends SpringBootServletInitializer implements WebApplicationInitializer {
#Autowired
private static JdbcTemplate jdbcTemplate;
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
public static void update(String query) {
jdbcTemplate.update(query);
}
}
#RestController
#RequestMapping("/query")
public class CrudController {
#RequestMapping(value="/update", method=RequestMethod.GET)
public String update(#PathVariable String tableName, /* other params */) {
// Generates query from params
Application.update(query);
return query;
}
}
I've also added an application.properties file which contains a single line in accordance with the Spring reference guide:
spring.datasource.jndi-name=jndiName
At this point, the WAR can still deploy onto the server but when I go to http://ipaddr:port/appName/query/update?params I get a NullPointerException. I've separately verified that update() correctly generates a SQL query with valid syntax, so I suspect I've gotten the database configuration wrong.
What's the correct way to connect to the JNDI database and execute a query?
Edit:
After updating my code according to Strelok's answer, I tried to run the updated WAR file on the WebLogic server, which subsequently throws the following exception:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'crudController': Injection of autowired dependencies failed; ...
...
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.jdbc.core.JdbcTemplate main.java.controllers.CrudController.jdbcTemplate; ...
...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.jdbc.core.JdbcTemplate] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I tried modifying the #Autowired annotation to #Autowired(required=true) but that didn't change anything. How can I resolve this?
Your JdbcTemplate is static and is located in your application class, whereas it should belong in your controller. That's where you should use it.
Spring cannot inject static fields directly, so make sure anything injected by Spring is never static. Your code should look something like this:
Application.java
#SpringBootApplication
public class Application extends SpringBootServletInitializer implements WebApplicationInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
CrudController.java
#RestController
#RequestMapping("/query")
public class CrudController {
#Autowired
private JdbcTemplate jdbcTemplate;
#RequestMapping(value="/update", method=RequestMethod.GET)
public String update(#PathVariable String tableName, /* other params */) {
jdbcTemplate.update(query);
return query;
}
}
Related
I am using Alfresco Process Services and have created a created a spring boot project for custom logic like TaskListeners and Delegations. I am creating the jar file from this maven project and copying it into webapps/activiti-app/WEB-INF/lib folder.
I have a simple TaskListener as below which is getting called on Task start. But the #Autowired variables are always null.
package com.activiti.extension.bean;
#Component("myTaskListener")
public class MyTaskListener implements TaskListener {
#Autowired
UserService userService;
#Override
public void notify(DelegateTask task) {
logger.info("userService: " +userService); // Always prints null
}
Finally I was able to make it work. I was putting the task listener in the class field of the Task properties with full package name. Now I am putting Delegate expression like ${myTaskListener} and it worked...
Thank you all for your time and help
This is because your your MyTaskListener is annotated as #Component or at least being ignored by spring during init. for auto-wiring capabilities spring requires this annotation (or similar to this) under the provided #ComponentScan packages to consider the class as a bean otherwise it will take as a normal java class and hence the #autowired is of no use in your case.
This below code is worked for me
#Component
public class MyTaskListener implements TaskListener {
public static UserService getUserServiceObject() {
return SpringApplicationContextHolder.getApplicationContext().getBean(UserService.class);
}
#Override
public void notify(DelegateTask delegateTask) {
//UserService Object, It is not null now
getUserServiceObject();
}
}
#Component
public class SpringApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
There is also one more way to get to your custom service "UserService" using Alfresco Spring Application context.
First access ServiceRegistry (registry used for accessing Alfresco Services but also any other custom service):
ServiceRegistry serviceRegistry = (ServiceRegistry) Context.getProcessEngineConfiguration().getBeans().get(ActivitiConstants.SERVICE_REGISTRY_BEAN_KEY);
Then get custom service UserService:
QName qname = QName.createQName("UserService");
UserService userService = (UserService) serviceRegistry.getService(qname);
I'm running a spring boot project with a large number of test and I want to use extent report. I have created a TestExecutionListener and I use the setting in one test.
I don't want to copy the same annotation in all test.
Where do I need to set the TestExecutionListener? and how?
Is it possible to set up in the application.properties?
The spring-test module declares all default TestExecutionListeners in its META-INF/spring.factories properties file.
Creating a simple Spring application:
MyBean:
#Component
public class MyBean {
public void doSomething() {
System.out.println("-- in MyBean.doSomething() method --");
}
}
AppConfig:
#Configuration
#ComponentScan
public class AppConfig {
}
Writing a TestExecutionListener:
public class MyListener implements TestExecutionListener {
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
System.out.println("MyListener.beforeTestClass()");
}
#Override
public void prepareTestInstance(TestContext testContext) throws Exception {
System.out.println("MyListener.prepareTestInstance()");
}
#Override
public void beforeTestMethod(TestContext testContext) throws Exception {
System.out.println("MyListener.beforeTestMethod()");
}
#Override
public void afterTestMethod(TestContext testContext) throws Exception {
System.out.println("MyListener.afterTestMethod()");
}
#Override
public void afterTestClass(TestContext testContext) throws Exception {
System.out.println("MyListener.afterTestClass");
}
}
Writing JUnit test:
A TextExecutionListener can be registered via #TestExecutionListeners annotation:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = AppConfig.class)
#TestExecutionListeners(value = {MyListener.class,
DependencyInjectionTestExecutionListener.class})
#TestPropertySource("/test.properties")
public class MyTests {
#Autowired
private MyBean myBean;
#Test
public void testDoSomething() {
myBean.doSomething();
}
}
Detailed infomation: https://www.logicbig.com/tutorials/spring-framework/spring-core/test-execution-listener.html
EDIT:
Declaring test property sources
Test properties files can be configured via the locations or value attribute of #TestPropertySource as shown in the following example.
Both traditional and XML-based properties file formats are supported — for example, "classpath:/com/example/test.properties" or "file:///path/to/file.xml".
Each path will be interpreted as a Spring Resource. A plain path — for example, "test.properties" — will be treated as a classpath resource that is relative to the package in which the test class is defined. A path starting with a slash will be treated as an absolute classpath resource, for example: "/org/example/test.xml". A path which references a URL (e.g., a path prefixed with classpath:, file:, http:, etc.) will be loaded using the specified resource protocol. Resource location wildcards (e.g. */.properties) are not permitted: each location must evaluate to exactly one .properties or .xml resource.
#ContextConfiguration
#TestPropertySource("/test.properties")
public class MyTests {
// class body...
}
More info: https://docs.spring.io/autorepo/docs/spring-framework/4.2.0.RC2/spring-framework-reference/html/integration-testing.html
https://www.baeldung.com/spring-test-property-source
This is driving me nuts. I have the following files, it is a very simple setup.
public class MainApp {
public static void main(String[] args) {
//read the spring config java class
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("Config.class");
//System.out.println("Bean names: " + Arrays.toString(context.getBeanNamesForType(AccountDAO.class)));
//get the bean from spring container
AccountDAO accountDAO = context.getBean("accountDAO", AccountDAO.class);
//call the business method
accountDAO.addAccount();
//close the spring context
context.close();
}
}
Config.java:
#Configuration
#ComponentScan("com.aop")
#EnableAspectJAutoProxy
public class Config {
}
LoggingAspectDemo.java:
#Aspect
#Component
public class LoggingAspectDemo {
//this is where we add all our related advices for the logging
//let's start with an #Before advice
#Before("execution(public void addAccount())")
public void beforeAddAccountAdvice() {
System.out.println("\n=======>>>> Executing #Before advice on method addAccount() <<<<========");
}
}
AccountDAO.java
#Component
public class AccountDAO {
public void addAccount() {
System.out.println(getClass() + ": Doing my Db work: Adding an account");
}
}
Everytime I run the MainApp.java, I get:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'accountDAO' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1207)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
All the files are under "com.aop" package so #ComponentScan should be scanning all the components. It looks simple enough but I can't get my hands around the problem, can anyone help me where I am going wrong?
You're invoking the constructor of AnnotationConfigApplicationContext with "Config.class" as String argument, but this constructor is actually for invoking with base packages i.e. the argument must be a package name.
Since you want to use it with the Configuration class, use the constructor which accepts Class instance instead i.e.
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
I am trying to use an autowired reference from main class and am facing :
Cannot make a static reference to the non-static field
zipCodeLookupService.
This is obvious. But I want to know how to handle this situation. What is the correct way of autowiring when main class is involved. My code will be as below -
Interface class
package com.example.services;
public interface IZipCodeLookup {
String retriveCityForZip(String zipCode);
}
Service Class
package com.example.services;
import org.springframework.stereotype.Service;
#Service
public class ZipCodeLookupService implements IZipCodeLookup {
#Override
public String retriveCityForZip(String zipCode) {
//below is mock code. actual code does a db lookup using a DAO.
if(zipCode=="94123") return "San Francisco";
return "not found in DB";
}
}
here is the main class that requires the service class
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.example.services.IZipCodeLookup;
#SpringBootApplication
public class AutowireWithMainClassApplication {
#Autowired
IZipCodeLookup zipCodeLookupService;
public static void main(String[] args) {
SpringApplication.run(AutowireWithMainClassApplication.class, args);
String city;
//this will not work, compilation error
//Cannot make a static reference to the non-static field zipCodeLookupService
city=zipCodeLookupService.retriveCityForZip(args[0]);
System.out.println("city for zipcode " + args[0] + " is " +city);
}
}
Could someone suggest - how or what is the correct way of using autowiring when main class is involved.
(As making the Autowired reference as static does not work anyway)
in AutowireWithMainClassApplication class, changing to -
#Autowired
static IZipCodeLookup zipCodeLookupService;
throws
Exception in thread "main"
java.lang.NullPointerException
You can do one of the following:
Use the #Autowired object in a #PostConstruct method, which is executed after dependency injection is done, as davidxxx explained above
Use Spring's getBean() in your main() to explicitly ask Spring framework to return the object after the injection completes:
public static void main(String[] args) {
...
ConfigurableApplicationContext appContext = SpringApplication.run(StartApplication.class, args);
IZipCodeLookup service = appContext.getBean(IZipCodeLookup.class);
...
}
Use Spring's CommandLineRunner component (runs right after main), which will be responsible on autowiring your object:
#Component
public class MyRunner implements CommandLineRunner {
#Autowired
private IZipCodeLookup service;
#Override
public void run(String... args) throws Exception {
...
service.doSomething();
...
}
}
Implement Spring's ApplicationRunner's run method in your main:
#SpringBootApplication
public class StartApplication implements ApplicationRunner {
#Autowired
private IZipCodeLookup service;
public static void main(String[] args) {
ConfigurableApplicationContext appContext = SpringApplication.run(StartApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
...
service.doSomething();
...
}
}
A class annotated with a #SpringBootApplication annotation is not a classic bean.It creates the Spring context from a static method.
But autowired dependencies cannot be static.
That's why this statement :
city=zipCodeLookupService.retriveCityForZip(args[0]);
doesn't throw a Spring exception but a classic NullPointerException as you declare zipCodeLookupService as a static field.
In your case, as workaround, you could move the processing that uses the Spring bean in a instance method annotated with javax.annotation.PostConstruct method inside your main class and store the arguments passed to the main() method in a field in order to be able to use it later :
private static String[] args;
#Autowired
IZipCodeLookup zipCodeLookupService;
public static void main(String[] args) {
AutowireWithMainClassApplication.args = args;
SpringApplication.run(AutowireWithMainClassApplication.class, args);
}
#PostConstruct
public void init() {
String city=zipCodeLookupService.retriveCityForZip(args[0]);
System.out.println("city for zipcode " + args[0] + " is " +city);
}
To answer to your comment, you should note several things about #PostConstruct
1) It is not an annotation specific to Spring. So, the official documentation may discuss about things more general than Spring or specific but different things such as EJB (it was originally introduced for them).
2) The first sentence of the javadoc summarizes the general expected behavior.
The PostConstruct annotation is used on a method that needs to be
executed after dependency injection is done to perform any
initialization.
But this sentence
"executed after dependency injection is done"
means indeed :
"executed after all dependency injections are done"
We talk about dependency injection in general, not about each dependency injection.
So, yes stick you to that.
Applying it to your case should make things clearer.
The AutowireWithMainClassApplication class is considered as a Spring bean as #SpringBootApplication is annotated with #Configuration that is itself annotated with #Component.
And as any Spring bean, it may declare dependency injection.
That is a dependency injection :
#Autowired
IZipCodeLookup zipCodeLookupService;
But you could of course declare as many dependency injections that you want to :
#Autowired
IZipCodeLookup zipCodeLookupService;
#Autowired
OtherClass otherClass;
...
So only as all dependencies are effectively injected, the PostConstructwill be invoked one and once.
This is a follow up to the question Making spring-data-mongodb multi-tenant
Oliver Gierke explained options how to set-up multi-tenancy for a SpringDataMongo application. I followed his "collection approach" and was quite successful. So far. Problems arise, when I want to customise the MongoTemplate used. Have a look on this example:
#SpringBootApplication
public class MultiTenantMongoApplication {
public static void main(String[] args) {
SpringApplication.run(MultiTenantMongoApplication.class, args);
}
#Bean
public MongoTemplate mongoTemplate(Mongo mongo, #Value("${random.name}") String randomName) throws Exception {
String dbname = "db_" + randomName;
MongoTemplate mongoTemplate = new MongoTemplate(mongo, dbname) {
#SuppressWarnings("unused")
public void shutdown() {
mongo.dropDatabase(dbname);
}
};
return mongoTemplate;
}
}
#Document(collection="#{tenantProvider.getTenantCollectionName('Metric')}")
public class Metric {
}
#Repository
public interface MetricRepository extends MongoRepository<Metric, ObjectId>{}
#Component
public class TenantProvider {
public String getTenantCollectionName(String collectionName) {
...
}
}
This yields the following error:
SpelEvaluationException: EL1007E: Property or field 'tenantProvider'
cannot be found on null
When I remove the definition of the MongoTemplate bean in the application class everything is fine and runs as desired.
Obviously the property provider gets not configured appropriately, when the MongoTemplate is customised. Why is this happening? And what can I do, to get the property in place?
I think the above error is because of the SpEL expression. You can try this way to access the TenantProvider class using the below SpEL expression.
#{T(TenantProvider).getTenantCollectionName('Metric')}
or you can add a fully qualified class name for TenantProvider in the above expression.