Spring Boot can't autowire #ConfigurationProperties - java

Here is my FileStorageProperties class:
#Data
#ConfigurationProperties(prefix = "file")
public class FileStorageProperties {
private String uploadDir;
}
This gives me saying : not registered via #enableconfigurationproperties or marked as spring component.
And here is my FileStorageService :
#Service
public class FileStorageService {
private final Path fileStorageLocation;
#Autowired
public FileStorageService(FileStorageProperties fileStorageProperties) {
this.fileStorageLocation = Paths.get(fileStorageProperties.getUploadDir())
.toAbsolutePath().normalize();
try {
Files.createDirectories(this.fileStorageLocation);
} catch (Exception ex) {
throw new FileStorageException("Could not create the directory where the uploaded files will be stored.", ex);
}
}
public String storeFile(MultipartFile file) {
// Normalize file name
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
try {
// Check if the file's name contains invalid characters
if(fileName.contains("..")) {
throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
}
// Copy file to the target location (Replacing existing file with the same name)
Path targetLocation = this.fileStorageLocation.resolve(fileName);
Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
return fileName;
} catch (IOException ex) {
throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
}
}
public Resource loadFileAsResource(String fileName) {
try {
Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
Resource resource = new UrlResource(filePath.toUri());
if(resource.exists()) {
return resource;
} else {
throw new MyFileNotFoundException("File not found " + fileName);
}
} catch (MalformedURLException ex) {
throw new MyFileNotFoundException("File not found " + fileName, ex);
}
}
}
This gives me error saying : could not autowire no beans of type found.
And here is my project structure :
And when I try to run it, it gives me :
APPLICATION FAILED TO START
Description:
Parameter 0 of constructor in com.mua.cse616.Service.FileStorageService required a bean of type 'com.mua.cse616.Property.FileStorageProperties' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.mua.cse616.Property.FileStorageProperties' in your configuration.
How can I resolve this?

This is expected as #ConfigurationProperties does not make a class a Spring Component. Mark the class with #Component and it should work. Note that a class can only be injected if it is a Component.
Edit: From Spring 2.2+ (Reference)
#ConfigurationProperties scanning
Classes annotated with #ConfigurationProperties can now be found via classpath scanning as an alternative to using #EnableConfigurationProperties or #Component. Add #ConfigurationPropertiesScan to your application to enable scanning.

Try to annotate with #ConfigurationProperties and #Component
In here , Spring Boot #ConfigurationProperties is annotation for externalized configuration.if you are trying to inject property value from a property file to a class, you can add #ConfigurationProperties at a class level with stereotype annotations such as #Component or add #ConfigurationProperties to a #Bean method.

add bellow annotation in FileStorageProperties class:
#Component

Related

How can I read resources file from outside project directory?

Below code works fine if file location is resources folder but when file location is outside the project directory like(c:\file.json) it fails.
How can we load file from outside project directory.
#Bean
public UserInfo readFile() {
String fileName="prop.json";
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
File file = new File(classLoader.getResource(fileName).getFile());
try {
UserInfo info= new ObjectMapper(new JsonFactory()).readValue(file, UserInfo.class);
} catch (Exception e) {
}
return info;
}
You should create a Configuration class that implements WebMvcConfigurer and
override the addResourceHadndler Method to add new resource to spring context.
#Configuration
#EnableWebMvc
public class MvcConfig implements WebMvcConfigurer{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// register you resource here
}
}

Spring Boot "FirebaseApp with name [DEFAULT] doesn't exist."

Launching Spring Boot's jar file throws me these errors:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'temperatureController' defined in URL <...>
Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'temperatureService' defined in URL <...>
Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.temperaturetracker.services.TokenService]: Constructor threw exception; nested exception is java.lang.IllegalStateException: FirebaseApp with name [DEFAULT] doesn't exist.
Each class contains appropriate #: #Service or #RestController or #SpringBootApplication or #Entity or #Repository.
Some classes:
#Service
public class TemperatureService {
private final AlertService alertService;
#Autowired
public TemperatureService(AlertService alertService) {
this.alertService = alertService;
}
<...>
}
#Service
class AlertService #Autowired constructor(private val tokenService: TokenService,
private val cloudMessagingService: CloudMessagingService) {
#PostConstruct
fun initialize() {
<...>
}
}
#Service
public class CloudMessagingService {
final Logger logger = LoggerFactory.getLogger(CloudMessagingService.class);
public void sendFirebaseMessage() {
<...>
try {
var response = FirebaseMessaging.getInstance().send(fbMessage);
logger.debug("Notification response: " + response);
} catch (FirebaseMessagingException e) {
e.printStackTrace();
logger.error("Error sending Firebase Cloud Message: " + e);
}
}
}
#Service
public class FirebaseInitialize {
#PostConstruct
public void initialize() {
try {
FileInputStream serviceAccount =
new FileInputStream("hidden-path");
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.setDatabaseUrl("hidden-path")
.build();
FirebaseApp.initializeApp(options);
} catch (Exception e) {
e.printStackTrace();
}
}
}
#SpringBootApplication
public class TemperatureTrackerApplication {
public static void main(String[] args) {
SpringApplication.run(TemperatureTrackerApplication.class, args);
}
}
These errors occurs only when I launch my jar file. Running app via green arrow or Shift + F10 everything works perfectly.
Make sure your Firebase configuration is ok because the error is thrown when SpringBoot try to execute the class
FirebaseInitialize
I've changed my class's FirebaseInitialize method initialize() to:
try {
ClassPathResource serviceAccount =
new ClassPathResource("myFile.json"); // it is in resources folder
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount.getInputStream()))
.setDatabaseUrl("database-path-provided-by-firebase.app")
.build();
FirebaseApp.initializeApp(options);
} catch (Exception e) {
e.printStackTrace();
}
FileInputStream I've used before expected the resource to be on the file system, which cannot be nested in a jar file. Thus, using getInputStream() of ClassPathResource worked.
Please read more: Classpath resource not found when running as jar
Use a #PostConstruct method to initialize Firebase inside your #SpringBootApplication class. So the case above should become
#SpringBootApplication
public class TemperatureTrackerApplication {
public static void main(String[] args) {
SpringApplication.run(TemperatureTrackerApplication.class, args);
}
#PostConstruct
public void initialize() {
try {
FileInputStream serviceAccount =
new FileInputStream("hidden-path");
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.setDatabaseUrl("hidden-path")
.build();
FirebaseApp.initializeApp(options);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Access server.servlet.context-path from #Configuration

I'm new to spring-boot and I'm setting up a new server.
My company separate configurations in different files (e.g.: jdbc.properties, smtp.properties, etc etc..). All configurations are put in the folder "property-files" into Tomcat folder (as sibling of "webapps" folder) and into specialized folder with name of given applicaion; for example if my application is called:"wonderful-server" all my configuration files will be in:"#TomcatFolder/property-files/wonderful-server/".
My idea is to access property files with absolute file path, like this: "file:${catalina.home}/property-files#{server.servlet.context-path}/smtp.properties".
But if I try to access "server.servlet.context-path" from a #Configuration class I obtain null.
I've tried to put into application.properties:
server.servlet.context-path=/wonderful-server
and add to my #Configuration class:
#Value("${server.servlet.context-path=/wonderful-server}") String contextPath;
but when spring bootup, contextPath contain null. The same if I use # instead of $.
Then I've tried to put into the main of my #SpringBootApplication class:
System.setProperty("server.servlet.context-path", "/formx-server");
and use in my #Configuration class:
String contextPath = System.getProperty("server.servlet.context-path");
but when spring bootup, contextPath contain null. The same if I use:
#Value("#{systemProperties['server.servlet.context-path']}") private String contextPath;
or:
#Value("#{server.servlet.context-path}") private String contextPath;
My #configuration classes are really simple, for example:
#Configuration
public class EmailConfig {
#Bean
public JavaMailSender getJavaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
try {
Properties props = PropertiesLoaderUtils.loadProperties( new FileSystemResource(System.getProperty("catalina.home")+"/property-files/wonderful-server/smtp.properties"));
mailSender.setHost(props.getProperty("mail.host"));
mailSender.setPort(Integer.parseInt(props.getProperty("mail.port")));
mailSender.setUsername(props.getProperty("mail.username"));
mailSender.setPassword(props.getProperty("mail.password"));
Properties properties = mailSender.getJavaMailProperties();
properties.put("mail.transport.protocol", props.getProperty("mail.transport.protocol"));
properties.put("mail.smtp.auth", props.getProperty("mail.smtp.auth"));
properties.put("mail.smtp.starttls.enable", props.getProperty("mail.smtp.starttls.enable"));
properties.put("mail.debug", props.getProperty("mail.debug"));
} catch (IOException e) {
// e.printStackTrace();
LOG.error("Error to send email: "+e.getMessage());
}
return mailSender;
}
}
In this class I've used an absolute path with static context-path, I'm trying to use it instead as variable.
In advance: thank you everyone for your precious time.
You are using =instead of :
Inside Properties:
server.servlet.context-path=/wonderful-server
But inside Configuration:
Default Value follows after ::
#Value("${server.servlet.context-path:/wonderful-server}") String contextPath;

add directory to classpath in runtime

In my current spring project, when I run the application, it's created a directory on the user's home directory where I store some configuration files (*.properties file). In my code, I refer to this file in that way:
private String getFilename() {
return System.getProperty("user.home")+File.separator+".webapp"+File.separator+"webapp.preferences";
}
which allows me run the application in any operational system without change the code. I need add this directory to the classpath of the application, to allow me use the annotation PropertySource to access the properties stored in the file using either the method getproperty from Enviroment class or the Value annotation.
I using spring-boot, so the start point for the application it's that:
#Controller
#EnableJpaRepositories
#EnableAutoConfiguration
#ComponentScan(value="com.spring")
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
I also have classes WebAppInitializer, WebAppConfig and DispatcherConfig to store the configurations handled by the XML files web.xml and dispatcher-config.xml from spring.
Anyone can tell if this is possible and hor to accomplish that?
UPDATE
Following the sugestions in the commentaries, I add to my project this bean:
#Bean
static PropertySourcesPlaceholderConfigurer property() throws Exception {
PropertySourcesPlaceholderConfigurer propertyConfigurer = new PropertySourcesPlaceholderConfigurer();
String filename = System.getProperty("user.home")+File.separator+".webapp"+File.separator+"webapp.preferences";
File file = new File( filename );
if(file.exists())
propertyConfigurer.setLocation( new FileSystemResource( filename ) );
else {
if(file.mkdir()) {
FileOutputStream fos = new FileOutputStream( filename );
fos.close();
propertyConfigurer.setLocation( new FileSystemResource( filename ) );
}
}
return propertyConfigurer;
}
and try use this in my pojo class:
#Input(label = "Titulo")
#Property(key = "geral.titulo")
#Value(value = "${geral.titulo}")
private String titulo;
but when I create a new instance of this classe, the fields don't receive the value indicated by the annotation. What I am doing wrong? I verify the file and the properties exists in it.

#Value annotations inside my Java class don't load values from .properties file

Before asking this question I tried to follow the following questions which are similar:
Injecting Properties using Spring & annotation #Value
How can I inject a property value into a Spring Bean which was configured using annotations?
Loading up properties file to a class in Spring
However, in my case I am not using any web applications or Tomcat; I'm just trying to load a cluster.properties file into a regular Java project via Spring so I can then ingest dummy data into Accumulo. Also, I'm trying to load properties from a cluster.properties file, not from key value pairs defined in an xml file.
Using what I learned from the links above and lots of reading on Spring, here's what I have:
I created the following context.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<!-- Define the Spring Bean to load our cluster properties -->
<bean id="props" class="accumuloIngest.LoadProperties"></bean>
</beans>
And here is a small snippet of what my cluster.properties file looks like:
cluster.instance=instance
cluster.username=user
etc...
Next, I created the following Spring main method under the class MainApp.java:
package accumuloIngest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
// Spring main method used to load a cluster.properties file with the Spring framework
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
LoadProperties myObj = LoadProperties.class.cast(ctx.getBean("props"));
// Now print out the cluster.properties loaded by Spring to verify they aren't null
StringBuffer springPropsBuffer = new StringBuffer();
springPropsBuffer.append("Printing out cluster.properties read via Spring...");
springPropsBuffer.append("\n\n");
springPropsBuffer.append("instanceName= ");
springPropsBuffer.append(myObj.getInstanceName());
springPropsBuffer.append("\n");
springPropsBuffer.append("userName= ");
springPropsBuffer.append(myObj.getUserName());
springPropsBuffer.append("\n");
springPropsBuffer.append("password= ");
springPropsBuffer.append(myObj.getPassword());
springPropsBuffer.append("\n");
springPropsBuffer.append("zooServers= ");
springPropsBuffer.append(myObj.getZooServers());
springPropsBuffer.append("\n");
springPropsBuffer.append("tableName= ");
springPropsBuffer.append(myObj.getTableName());
springPropsBuffer.append("\n");
springPropsBuffer.append("dataFile= ");
springPropsBuffer.append(myObj.getDataFile());
springPropsBuffer.append("\n");
springPropsBuffer.append("dataDelim= ");
springPropsBuffer.append(myObj.getDataDelim());
springPropsBuffer.append("\n");
springPropsBuffer.append("rowCount= ");
springPropsBuffer.append(myObj.getRowCount());
springPropsBuffer.append("\n");
System.out.println(springPropsBuffer.toString());
// now start data ingest
myObj.startIngest(); // method that calls Ingester class to start data ingest
} // end of main method
} // end of MainApp class
Spring loads my context.xml file and loads the Bean I called "props", but the values are still null. It seems that my #Value annotations aren't working in my LoadProperties class:
package accumuloIngest;
import java.io.IOException;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.TableExistsException;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class LoadProperties {
// this class defines the Spring Bean and loads the cluster properties
// using the SpringFramework
#Bean
public static PropertyPlaceholderConfigurer props(){
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Resource[] resource = new ClassPathResource[ ]
{ new ClassPathResource("/EclipseProjectName/src/cluster.properties") };
ppc.setLocations(resource);
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
// Now load the properties from cluster.properties using the Spring Framework
private #Value("${cluster.instance}") String instanceName;
private #Value("${cluster.username}") String userName;
private #Value("${cluster.password}") String password;
private #Value("${cluster.zooServers}") String zooServers;
private #Value("${cluster.TableName}") String tableName;
private #Value("${cluster.DataFile}") String dataFile;
private #Value("${cluster.DataDelimiter}") String dataDelim;
private #Value("${cluster.rowCount}") int rowCount;
// Getters for the other Java classes to access properties loaded by Spring
public String getInstanceName() {
return instanceName;
}
public String getUserName() {
return userName;
}
public String getPassword() {
return password;
}
public String getZooServers() {
return zooServers;
}
public String getTableName() {
return tableName;
}
public String getDataFile() {
return dataFile;
}
public String getDataDelim() {
return dataDelim;
}
public int getRowCount() {
return rowCount;
}
// method to kick off the ingest of dummy data
void startIngest() {
Ingester ingestObject = new Ingester();
try {
ingestObject.ingestData();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TableNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TableExistsException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AccumuloException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AccumuloSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // end of try-catch block
} // end of startIngest method
} // end of LoadProperties class
Yet when I run MainApp.java in Eclipse the values are null when my Ingester.java class calls the getters.
Here's the console output when I run MainApp.java in Eclipse:
13/09/24 14:08:24 INFO support.ClassPathXmlApplicationContext: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext#191f667c: startup date [Tue Sep 24 14:08:24 EDT 2013]; root of context hierarchy
13/09/24 14:08:24 INFO xml.XmlBeanDefinitionReader: Loading XML bean definitions from class path resource [context.xml]
13/09/24 14:08:24 INFO support.DefaultListableBeanFactory: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#3cdd17f5: defining beans [props]; root of factory hierarchy
Printing out cluster.properties read via Spring...
instanceName= null
userName= null
password= null
zooServers= null
tableName= null
dataFile= null
dataDelim= null
rowCount= 0
Exception in thread "main" java.lang.IllegalArgumentException: argument was null:Is null- arg1? true arg2? true
at org.apache.accumulo.core.util.ArgumentChecker.notNull(ArgumentChecker.java:36)
at org.apache.accumulo.core.client.ZooKeeperInstance.<init>(ZooKeeperInstance.java:99)
at org.apache.accumulo.core.client.ZooKeeperInstance.<init>(ZooKeeperInstance.java:85)
at accumuloIngest.Ingester.ingestData(Ingester.java:65)
at accumuloIngest.LoadProperties.startIngest(LoadProperties.java:69)
at accumuloIngest.MainApp.main(MainApp.java:44)
Am I missing a piece of the Spring framework that loads the properties in my cluster.properties file? I had tried adding #AutoWired to both my MainApp and LoadProperties java classes but that didn't seem to help.
If you're going to use #Bean, you'll need #Configuration. You shouldn't declare a xml context to include an annotation context. You also shouldn't use your #Configuration class instance as a bean. ClassPathXmlApplicationContext is no good for processing annotation based configurations.
Use something like the following
#Configuration
#ComponentScan(basePackageClasses = LoadProperties.class)
public static class Config {
#Bean
public static PropertyPlaceholderConfigurer props() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Resource[] resource = new ClassPathResource[] { new ClassPathResource(
"/EclipseProjectName/src/cluster.properties") };
ppc.setLocations(resource);
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
#Bean
public LoadProperties loadProperties() {
return new LoadProperties();
}
}
public static class LoadProperties {
private #Value("${cluster.zooServers}") String zooServers;
... // getters and setters
}
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
LoadProperties load = (LoadProperties) context.getBean(LoadProperties.class);
System.out.println(load.getZooServers());
}
A few things to note:
In your ClassPathResource you need to specify a classpath resource. Do you really have a resource /EclipseProjectName/src/cluster.properties at the root of your classpath? I very much doubt it.
In this case you won't need a #ComponentScan, but familiarize yourself with it.
A PropertyPlaceholderConfigurer needs to be declared static so it can be initialized before the other #Bean declarations. You should use PropertySourcesPlaceholderConfigurer as explained in the javadoc.

Categories

Resources