I need to read a properties file in a glassfish 4 application. The file needs to be somewhere in the application (i.e. not at some random place in the file system).
If it matters, I'm developing with eclipse, the project builds with maven, and the artifact is a war.
It seems to me there are three things I need to know to make this work.
1) Where does the original file need to be?
2) Where does the file need to end up?
3) How do I read it?
So far I created the file:
src/main/resources/version.properties
which ends up in
WEB-INF/classes/version.properties
I don't know if that is the correct location.
Based on similar questions, I have defined a ServletContextListener:
public class ServletContextClass implements ServletContextListener {
...
#Override
public void contextInitialized(ServletContextEvent arg0) {
ServletContext ctx = arg0.getServletContext();
InputStream istream = ctx.getResourceAsStream("version.properties");
// at this point, istream is null
Properties p = new Properties();
p.load(istream);
}
}
I'm not sure if I have the file in the wrong place, if I'm reading it wrong, or both.
update: the following "works":
#Override
public void contextInitialized(ServletContextEvent arg0) {
ResourceBundle bundle = ResourceBundle.getBundle("version");
if (bundle == null) {
logger.info("bundle is null");
} else {
logger.info("bundle is not null");
logger.info("version: " + bundle.getString("myversion"));
}
}
However, I don't think this is the correct solution. Bundles are for locale support, and this does not fall under that category.
Update 2: I corrected the location where the file ends up.
1) Putting the version.properties file in
src/main/resources/version.properties
seems to be correct.
2) In the target war, the file does in fact end up in
WEB-INF/classes/version.properties
3) To read the file: I already had a ServletContextListener defined. If you don't you need to define one and configure it in web.xml. Here is a portion of my ServletContextListener:
package com.mycompany.service;
public class ServletContextClass implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent arg0) {
ServletContext ctx=arg0.getServletContext();
try {
Properties p = new Properties();
InputStream istream = ctx.getResourceAsStream("/WEB-INF/classes/version.properties");
p.load(istream);
Properties sysProps = System.getProperties();
sysProps.putAll(p);
} catch (IOException e) {
logger.error("Error reading " + "version.properties");
}
}
}
It is configured with this piece of the web.xml:
<listener>
<listener-class>com.mycompany.service.ServletContextClass</listener-class>
</listener>
Related
My web application has several integrations with external systems and all these integration Rest URLs are kept in a config file with in web app. My application reads this config file at start up and use the URL values while making connections to external systems. But quite often it happens that one of the external systems is down and we have to use an alternate URL. In that case, we typically will have to modify the config and redeploy the war file. Is there a way to modify config file with new value without going through a redeployment of the war file?
In my projects i usually work with Apache Commons Configuration for the management of config files (properties). This library has the capability of automatic reload the values when file changes.
This is muy suggestion of implementation:
Create a class "MyAppConfigProperties" for load the properties file and read your configuration keys:
public class MyAppConfig {
//Apache Commons library object
private PropertiesConfiguration configFile;
private void init() {
try {
//Load the file
configFile = new PropertiesConfiguration(
MyAppConfig.class.getClassLoader().getResource("configFile.properties"));
// Create refresh strategy with "FileChangedReloadingStrategy"
FileChangedReloadingStrategy fileChangedReloadingStrategy = new FileChangedReloadingStrategy();
fileChangedReloadingStrategy.setRefreshDelay(1000);
configFile.setReloadingStrategy(fileChangedReloadingStrategy);
} catch (ConfigurationException e) {
//Manage the exception
}
}
/**
* Constructor por defecto.
*/
public MyAppConfig() {
super();
init();
}
public String getKey(final String key) {
try {
if (configFile.containsKey(key)) {
return configFile.getString(key);
} else {
return null;
}
} catch (ConversionException e) {
//Manage Exception
}
}
}
Now you have to construct a instance of this class (singleton) and use it in all places in that you need to reed a config key.
Every time you use the method "getKey" you will get the last value of the key without deploy and restart.
When I build project by maven, it's OK, but when deploy it by Tomkat, I have NullPointerException.
Class, where can be problem - PropertiesManager.
logline: PropertiesManager.getApplicationProperties(PropertiesManager.java:31)
public class PropertiesManager {
private static final String PROPERTY_FILE_NAME =
"resources/application.properties";
private static PropertiesManager Instance;
private Properties properties;
private PropertiesManager() {
}
public static PropertiesManager getInstance() {
if (Instance == null) {
Instance = new PropertiesManager();
}
return Instance;
}
public Properties getApplicationProperties() {
if (properties == null) {
properties = new Properties();
try (InputStream stream = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream(PROPERTY_FILE_NAME)) {
properties.load(stream);
} catch (IOException e) {
throw new ApplicationException("Failed to load property file", e);
}
}
return properties;
}
}
And logline: ApplicationLifecycleListener.contextInitialized(ApplicationLifecycleListener.java:14)
Class ApplicationLifecycleListener:
public class ApplicationLifecycleListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
Properties applicationProperties = PropertiesManager.getInstance().getApplicationProperties();
DBManager.getInstance().initialize(applicationProperties);
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
DBManager.getInstance().stopDb();
}
}
What is problem can be?
Without providing the file with the exact line you see the NullPointerException (none of the files you provided have the lines shown in log), it is difficult to be sure. But one hint is that although you put your resources files to be built with Maven in the '<project>/src/main/resources' folder, when built and packing the war file, it will put your application resource files in the 'WEB-INF/classes' folder which is part of the application default classpath. Therefore, to correctly reference them using the method Thread.currentThread().getContextClassLoader().getResourceAsStream(...) you should not add the 'resources\...' prefix to the file name, since this method already look files in the default application classpath. Remove the prefix and see if it works. Please, refer to this answer for more detail.
First of, my sincere apologies for bringing up an oft repeated question in this forum; but I cannot figure out my mistake(s).
I have two .properties files that I am trying to load unsuccessfully. Here's the folder structure I have - unless there is a compelling reason otherwise or it is contrary to the best practice, I like to keep this structure:
As you notice my DAO code is under zencs.dbutils package and my .properties files are respectively under zencs.resources.properties.db* packages.
The reason I do it this way because eventually this will connect to and manage multiple data sources - my DAO code will evolve to handle them dynamically (not yet so). I want to set up all data source properties in one place
My Project properties are set as follows:
Now in my DAO class I have a method initProperties(), called by getConnection(), that is trying to reference these properties files through getResourceAsStream(). Please see below code that I tried:
public class DAO {
Connection conn = null;
public Properties properties = new Properties();
public Properties dbConnect = new Properties();
private void initProperties() {
InputStream inputDBdrivers = getClass().getResourceAsStream("snowflakeConnect.properties");
if (inputDBdrivers != null) {
try{
dbConnect.load(inputDBdrivers);
inputDBdrivers.close();
} catch(IOException ioex) {
System.err.println(ioex.getStackTrace().toString());
}
} else {
System.out.println("snowflakeConnect.properties file not found! Terminating Application normally...");
System.exit(0);
}
InputStream inputDBprops = getClass().getResourceAsStream("snowflake.properties");
if (inputDBprops != null) {
try{
properties.load(inputDBprops);
inputDBprops.close();
} catch(IOException ioex) {
System.err.println(ioex.getStackTrace().toString());
}
} else {
System.out.println("snowflake.properties file not found! Terminating Application normally...");
System.exit(0);
}
}
Connection getConnection() throws SQLException {
// build connection properties
initProperties();
try {
Class.forName(dbConnect.getProperty("driver"));
} catch (ClassNotFoundException cnfex) {
System.err.println("ERROR: getConnection() :: Snowflake Class not found: " + cnfex.getMessage());
}
return DriverManager.getConnection(dbConnect.getProperty("connectStr"), properties);
}
public DAO() {
try {
this.conn = getConnection();
} catch (SQLException sqlex) {
Logger.getLogger(DAO.class.getName()).log(Level.SEVERE, null, sqlex);
}
}
}
When I am executing it, the error says "snowflakeConnect.properties file not found! Terminating Application normally..."
My evaluation is that the code in the above form resolving the files to be in zencs/dbutils/ and the ClassLoader cannot find them there obviously.
I tried full absolute path (out of desperation though it expects relative); I tried relative path with "../resources/properties/{dbdrivers | dbutils}/filename.properties" with no success. With the relative path it is resolving to "zencs/dbutils/../resources/properties/dbdrivers/snowflakeConnect.properties" for ClassLoader...
Am I NOT setting the resources folder and everything underneath it correctly?
Obviously my comprehension of how it should resolve is flawed. Can you please help with what I might have not understood and how should I go about this issue?
Thanks a bunch!
You could try to use getResourceAsStream() including your package name like this:
InputStream inputDBdrivers = getClass().getResourceAsStream("/zencs/resources/properties/dbdrivers/snowflakeConnect.properties");
InputStream inputDBprops = getClass().getResourceAsStream("/zencs/resources/properties/dbutils/snowflake.properties");
The leading slash is usually the key part here. It could help to remove that as well but you said you've tried that already so I guess that's not what you're looking for.
I've been working on a plugin that requires a fair amount of data being stored.
I have it being stored in a custom config file I found online that works basically the same as the default config.
The problem I'm having is that I am not sure how to actually close the file or if I even need to, as I know little about yaml configurations.
The code for the template I used is below.
I'm also curious as to advice on how I should store larger amounts of data in the future.
public class CustomConfig {
//store name of file to load/edit
private final String fileName;
//store plugin, to get file directory
private final JavaPlugin plugin;
//store actual hard disk file location
private File configFile;
//store ram file copy location
private FileConfiguration fileConfiguration;
//constructor taking a plugin and filename
public CustomConfig(JavaPlugin plugin, String fileName) {
//ensure plugin exists to get folder path
if (plugin == null)
throw new IllegalArgumentException("plugin cannot be null");
//set this classes plugin variable to the one passed to this method
this.plugin = plugin;
//get name of file to load/edit
this.fileName = fileName;
//get directory/folder of file to load/edit
File dataFolder = plugin.getDataFolder();
if (dataFolder == null)
throw new IllegalStateException();
//load config file from hard disk
this.configFile = new File(plugin.getDataFolder(), fileName);
reloadConfig();
}
public void reloadConfig() {
//load memory file from the hard copy
fileConfiguration = YamlConfiguration.loadConfiguration(configFile);
// Look for defaults in the jar
File configFile = new File(plugin.getDataFolder(), fileName);
if (configFile != null) {
YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(configFile);
fileConfiguration.setDefaults(defConfig);
}
}
public FileConfiguration getConfig() {
if (fileConfiguration == null) {
this.reloadConfig();
}
return fileConfiguration;
}
public void saveConfig() {
if (fileConfiguration == null || configFile == null) {
return;
} else {
try {
getConfig().save(configFile);
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, "Could not save config to " + configFile, ex);
}
}
}
public void saveDefaultConfig() {
if (!configFile.exists()) {
this.plugin.saveResource(fileName, false);
}
}
}
No. You do not have to close YamlConfiguration objects.
While the default config (JavaPlugin.getConfig()) is bound to the lifecycle of the plugin, custom ones are disposed when any other Java object is, i.e. when the garbage collector determines that there are no more references pointing to them in the code.
You don't need to close the config. It's not a BufferedWriter. The config keeps all of the data in the memory until the server shuts down. This means that if you change something in the config during the time your plugin is enabled, you will need to use your reloadConfig() method. The only clean up you need to do after using the FileConfiguration#set(String, Object) method is to use FileConfiguration#saveConfig() to tell Bukkit to take the current state of your config and copy it into your config file.
Having a Java web application, how does one read properties file only once when the app is deployed (storing them later in some singleton)? Meaning, configuration changes would require redeployment.
Otherwise, is there an alternative way to prevent an app from constantly reading .properties file? Previously I had settings in my web.xml file, but now .properties is required.
Code used to read app settings from JBoss configuration path:
File f = new File(System.getProperty("jboss.server.config.dir"),
"myappsettings.properties");
Properties p = new Properties();
p.load(new FileInputStream(f));
try {
db_name = p.getProperty("DATABASE_NAME"));
file_path = p.getProperty("FILE_PATH"));
...
} catch (Exception e) {
...
}
Starting with JEE6, another alternative to the ServletContextListener could be using a singleton startup bean:
#Singleton
#Startup
public class PropertyRegistry {
#PostConstruct
private void init(){
//do something
}
}
Implement your own ServletContextListener:
public class PropertiesReadingListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
//read properties here...
event.
getServletContext().
setAttribute("db_name", p.getProperty("DATABASE_NAME"));
}
#Override
public void contextDestroyed(ServletContextEvent event) {
}
}
You must reference this class in web.xml:
<listener>
<listener-class>com.example.PropertiesReadingListener</listener-class>
</listener>