I've inherited some Java code. Several classes have their instance variables initialized from property values in /WEB-INF/servlet.properties like this:
@Value("${context.root}")
private String contextRoot;
When I try this in a new class, the instance variable is not initialized. My class is constructed similarly to the one that works, but it is in a different package (com.company.app.utilities vs. com.company.app.service) Both import the same class:
import org.springframework.beans.factory.annotation.Value;
Both have corresponding public getter and setter methods.
I've reviewed some Spring documentation and /WEB-INF/applicationContext.xml, but I don't see anything obvious that I need to configure.
Any assistance is greatly appreciated.
Update: I see the following entries in the log:
[localhost-startStop-1] 03 Dec 2014 02:47:10,791 INFO : org.springframework.context.support.PropertySourcesPlaceholderConfigurer - Loading properties file from ServletContext resource [/WEB-INF/servlet.properties]
[localhost-startStop-1] 03 Dec 2014 02:47:10,938 INFO : org.springframework.beans.factory.support.DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@601e8f7d: defining beans [...,contactServiceImpl,s3Transfer,...]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@32f5f812
I've omitted all other singletons from the log entry, to highlight that both the original class and my class are in the list; however, I created a constructor, whereas the original class has no constructor. The next line in the log is the exception I throw in a getter method when the value is null, caught inside the constructor:
[localhost-startStop-1] 03 Dec 2014 02:47:12,730 ERROR: com.company.app.utilities.S3Transfer - bucketName is null!
java.lang.Exception: bucketName is null!
at com.company.app.utilities.S3Transfer.getBucketName(S3Transfer.java:129)
at com.company.app.utilities.S3Transfer.<init>(S3Transfer.java:48)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:148)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1000)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:953)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:487)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:626)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:651)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:599)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:665)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:518)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:459)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
at javax.servlet.GenericServlet.init(GenericServlet.java:160)
...
Should I remove the constructor? If so, is there additional Spring configuration to instantiate a singleton of this class? Thanks.
Update 2014-12-03: I think I've been away from Java too long, as the last time I coded AOP, there was no Spring Framework, and it's got me a bit confused. When you say "instantiated by Spring", does this mean to place @Autowired in the class that uses my new class? I've made this change and I've rewritten my class to implement an interface, but now Tomcat fails to restart properly. Code, properties and logs below:
package com.company.app.utilities;
import com.company.app.bean.Contact;
import java.io.InputStream;
public interface S3Transfer {
String storeContactProfilePicture(Long idUser, Contact contact);
String storeContactProfilePicture(Long idUser, Long idContact, InputStream inStream);
String storeUserProfilePicture(Long idUser, String fileName, String accountType);
}
package com.company.app.utilities;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.company.app.bean.Contact;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
public class S3TransferImpl implements S3Transfer {
private final AmazonS3 s3client = new AmazonS3Client();
private final Logger logger = Logger.getLogger(getClass());
@Value("${context.root}")
private String contextRoot;
@Value("${s3.bucket.name}")
private String bucketName;
@Value("${contact.profile.picture}")
private String contactProfilePictureKey;
@Value("${user.profile.picture}")
private String userPictureKey;
public String storeContactProfilePicture(Long idUser, Contact contact) {
String keyName = getContactProfilePictureKey().replaceAll("<<idUsr>>", idUser.toString()).replaceAll("<<idContact>>", contact.getIdContact().toString());
String fileName = contact.getPicture();
storeObjectFromFileName(fileName, keyName);
return contextRoot + keyName;
}
public String storeContactProfilePicture(Long idUser, Long idContact, InputStream inStream) {
String keyName = getContactProfilePictureKey().replaceAll("<<idUsr>>", idUser.toString()).replaceAll("<<idContact>>", idContact.toString());
storeObject(inStream, keyName);
return contextRoot + keyName;
}
public String storeUserProfilePicture(Long idUser, String fileName, String accountType) {
String keyName = getUserPictureKey().replaceAll("<<idUsr>>", idUser.toString()).replace("<<socialNetwork>>", accountType);
storeObjectFromFileName(fileName, keyName);
return contextRoot + keyName;
}
private void storeObjectFromFileName(String fileName, String keyName) {
try {
logger.info("Uploading " + fileName + " to " + keyName);
InputStream inStream = new URL(fileName).openStream();
storeObject(inStream, keyName);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
private void storeObject(InputStream inStream, String keyName) {
try {
this.s3client.putObject(new PutObjectRequest(getBucketName(), keyName, inStream, null));
} catch (AmazonServiceException ase) {
logger.error("Caught an AmazonServiceException, which "
+ "means your request made it "
+ "to Amazon S3, but was rejected with an error response"
+ " for some reason.");
logger.error("Error Message: " + ase.getMessage());
logger.error("HTTP Status Code: " + ase.getStatusCode());
logger.error("AWS Error Code: " + ase.getErrorCode());
logger.error("Error Type: " + ase.getErrorType());
logger.error("Request ID: " + ase.getRequestId());
} catch (AmazonClientException ace) {
logger.error("Caught an AmazonClientException, which "
+ "means the client encountered "
+ "an internal error while trying to "
+ "communicate with S3, "
+ "such as not being able to access the network.");
logger.error("Error Message: " + ace.getMessage());
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
public String getContextRoot() {
return contextRoot;
}
public void setContextRoot(String contextRoot) {
this.contextRoot = contextRoot;
}
public String getBucketName() {
return bucketName;
}
public void setBucketName(String bucketName) {
this.bucketName = bucketName;
}
private String getContactProfilePictureKey() {
return contactProfilePictureKey;
}
private void setContactProfilePictureKey(String contactProfilePictureKey) {
this.contactProfilePictureKey = contactProfilePictureKey;
}
private String getUserPictureKey() {
return userPictureKey;
}
private void setUserPictureKey(String userPictureKey) {
this.userPictureKey = userPictureKey;
}
}
The classes that use S3Transfer now have the following code:
@Autowired
private S3Transfer s3Transfer;
Both /WEB-INF/applicationContext.xml and /WEB-INF/app-web-servlet.xml have the following elements within the complex bean element:
<context:annotation-config />
<context:component-scan base-package="com.company.app" />
<context:property-placeholder location="/WEB-INF/servlet.properties" />
When I restart Tomcat, initialization fails. Here are some of the relevant log entries:
[localhost-startStop-1] 03 Dec 2014 18:39:03,407 INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resource [/WEB-INF/applicationContext.xml]
[localhost-startStop-1] 03 Dec 2014 18:39:03,926 INFO : org.springframework.context.annotation.ClassPathBeanDefinitionScanner - JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning
[localhost-startStop-1] 03 Dec 2014 18:39:05,990 INFO : org.springframework.context.annotation.ClassPathBeanDefinitionScanner - JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning
[localhost-startStop-1] 03 Dec 2014 18:39:07,983 INFO : org.springframework.context.support.PropertySourcesPlaceholderConfigurer - Loading properties file from ServletContext resource [/WEB-INF/servlet.properties]
[localhost-startStop-1] 03 Dec 2014 18:39:08,708 INFO : org.springframework.beans.factory.support.DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@63655d7a: defining beans [... {long list, but s3TransferImpl is not in the list} ...]; root of factory hierarchy
[localhost-startStop-1] 03 Dec 2014 18:39:10,667 INFO : org.springframework.jdbc.datasource.DriverManagerDataSource - Loaded JDBC driver: com.mysql.jdbc.Driver
[localhost-startStop-1] 03 Dec 2014 18:39:20,152 ERROR: org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#0' while setting bean property 'sourceList' with key [0]; nested exception is ... {long list of nested exceptions}...; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.company.app.utilities.S3Transfer] 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)}
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:329)
Update 2014-12-03 14:50 EST: Apparently, both my interface and class needed an @Service annotation, as Tomcat restarted successfully. What is the purpose of this annotation?