I've got an AppEngine app with two different instances, one for prod and one for staging. Accordingly, I'd like to configure the staging instance slightly differently, since it'll be used for testing. Disabling emails, talking to a different test backend for data, that kind of thing.
My first intuition was to use a .properties file, but I can't seem to get it to work. I'm using Gradle as a build system, so the file is saved in src/main/webapp/WEB-INF/staging.properties (and a matching production.properties next to it). I'm trying to access it like so:
public class Config {
private static Config sInstance = null;
private Properties mProperties;
public static Config getInstance() {
if (sInstance == null) {
sInstance = new Config();
}
return sInstance;
}
private Config() {
// Select properties filename.
String filename;
if (!STAGING) { // PRODUCTION SETTINGS
filename = "/WEB-INF/production.properties";
} else { // DEBUG SETTINGS
filename = "/WEB-INF/staging.properties";
}
// Get handle to file.
InputStream stream = this.getClass().getClassLoader().getResourceAsStream(filename);
if (stream == null) {
// --> Crashes here. <--
throw new ExceptionInInitializerError("Unable to open settings file: " + filename);
}
// Parse.
mProperties = new Properties();
try {
mProperties.load(stream);
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
}
The problem is that getResourceAsStream() is always returning null. I checked the build/exploded-app directory, and the .properties file shows up there. I also checked the .war file, and found the .properties file there as well.
I've also tried moving the file into /WEB-INF/classes, but that didn't make a difference either.
What am I missing here?
Try
BufferedReader reader = new BufferedReader(new FileReader(filename));
or
InputStream stream = this.getClass().getResourceAsStream(filename);
Related
Is it possible to access Assets inside the Java code in Play Framework? How?
We access assets from the scala HTML templates this way:
<img src="#routes.Assets.versioned("images/myimage.png")" width="800" />
But I could not find any documentation nor code example to do it from inside the Java code. I just found a controllers.Assets class but it is unclear how to use it. If this is the class that has to be used, should it maybe be injected?
I finally found a way to access the public folder even from a production mode application.
In order to be accessible/copied in the distributed version, public folder need to be mapped that way in build.sbt:
import NativePackagerHelper._
mappings in Universal ++= directory("public")
The files are then accessible in the public folder in the distributed app in production form the Java code:
private static final String PUBLIC_IMAGE_DIRECTORY_RELATIVE_PATH = "public/images/";
static File getImageAsset(String relativePath) throws ResourceNotFoundException {
final String path = PUBLIC_IMAGE_DIRECTORY_RELATIVE_PATH + relativePath;
final File file = new File(path);
if (!file.exists()) {
throw new ResourceNotFoundException(String.format("Asset %s not found", path));
}
return file;
}
This post put me on the right way to find the solution: https://groups.google.com/forum/#!topic/play-framework/sVDoEtAzP-U
The assets normally are in the "public" folder, and I don't know how you want to use your image so I have used ImageIO .
File file = new File("./public/images/nice.png");
boolean exists = file.exists();
String absolutePath = file.getAbsolutePath();
try {
ImageInputStream input = ImageIO.read(file); //Use it
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("EX = "+exists+" - "+absolutePath);
using java 8, tomcat 8
Hi, i am loading a file using properties, but i have a check before loading which returns the same properties object if its already been loaded (not null). which is a normal case scenario but i want to know if there is any way that if any change occur in target file, and some trigger should be called and refreshes all the properties objects. here is my code.
public static String loadConnectionFile(String keyname) {
String message = "";
getMessageFromConnectionFile();
if (propertiesForConnection.containsKey(keyname))
message = propertiesForConnection.getProperty(keyname);
return message;
}
public static synchronized void getMessageFromConnectionFile() {
if (propertiesForConnection == null) {
FileInputStream fileInput = null;
try {
File file = new File(Constants.GET_CONNECTION_FILE_PATH);
fileInput = new FileInputStream(file);
Reader reader = new InputStreamReader(fileInput, "UTF-8");
propertiesForConnection = new Properties();
propertiesForConnection.load(reader);
} catch (Exception e) {
Utilities.printErrorLog(Utilities.convertStackTraceToString(e), logger);
} finally {
try {
fileInput.close();
} catch (Exception e) {
Utilities.printErrorLog(Utilities.convertStackTraceToString(e), logger);
}
}
}
}
the loadConnectionFile method executes first and calls getMessageFromConnectionFile which has check implemented for "null", now if we remove that check it will definitely load updated file every time but it will slower the performance. i want an alternate way.
hope i explained my question.
thanks in advance.
Java has a file watcher service. It is an API. You can "listen" for changes in files and directories. So you can listen for changes to your properties file, or the directory in which your properties file is located. The Java Tutorials on Oracle's OTN Web site has a section on the watcher service.
Good Luck,
Avi.
Please can you help me to read the properties from application.properties file in Spring Boot, without autowiring the Environment and without using the Environment?
No need to use ${propname} either. I can create properties object but have to pass my properties file path. I want to get my prop file from another location.
This is a core Java feature. You don't have to use any Spring or Spring Boot features if you don't want to.
Properties properties = new Properties();
try (InputStream is = getClass().getResourceAsStream("application.properties")) {
properties.load(is);
}
JavaDoc: http://docs.oracle.com/javase/8/docs/api/java/util/Properties.html
OrangeDog solution didn't work for me. It generated NullPointerException.
I've found another solution:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Properties properties = new Properties();
try (InputStream resourceStream = loader.getResourceAsStream("application.properties")) {
properties.load(resourceStream);
} catch (IOException e) {
e.printStackTrace();
}
Try to use plain old Properties.
final Properties properties = new Properties();
properties.load(new FileInputStream("/path/config.properties"));
System.out.println(properties.getProperty("server.port"));
In case you need to use that external properties file in your configuration it can be accomplished with #PropertySource("/path/config.properties")
The following code extracts the environment value from an existing application.properties file which is located in the Deployed Resources under WEB-INF/classes :
// Define classes path from application.properties :
String environment;
InputStream inputStream;
try {
// Class path is found under WEB-INF/classes
Properties prop = new Properties();
String propFileName = "com/example/project/application.properties";
inputStream = getClass().getClassLoader().getResourceAsStream(propFileName);
// read the file
if (inputStream != null) {
prop.load(inputStream);
} else {
throw new FileNotFoundException("property file '" + propFileName + "' not found in the classpath");
}
// get the property value and print it out
environment = prop.getProperty("environment");
System.out.println("The environment is " + environment);
} catch (Exception e) {
System.out.println("Exception: " + e);
}
Here is example, running the above code with the following input from the application.properties (Text file):
# Application settings file
environment=Test
release_date=DATE
session_timeout_minutes=25
## Allowable image types
img_file_extensions="jpeg;pjpeg;jpg;png;gif"
## Images are saved with this extension
img_default_extension=jpg
# Mail Settings / Addresses
mail_debug=false
Output:
The environment is Test
To read application.properties just add this annotation to your class:
#ConfigurationProperties
public class Foo {
}
If you want to change the default file
#PropertySource("your properties path here")
public class Foo {
}
If everything else is properly set, you can the annotation #Value. Springboot will take care of loading the value from property file.
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.beans.factory.annotation.Value;
#Configuration
#PropertySource("classpath:/other.properties")
public class ClassName {
#Value("${key.name}")
private String name;
}
Adding to Vladislav Kysliy's elegant solution, below code can be directly plugged as REST API Call to get all the key/value of application.properties file in Spring Boot without knowing any key. Additionally, If you know the Key you can always use #Value annotation to find the value.
#GetMapping
#RequestMapping("/env")
public java.util.Set<Map.Entry<Object,Object>> getAppPropFileContent(){
ClassLoader loader = Thread.currentThread().getContextClassLoader();
java.util.Properties properties = new java.util.Properties();
try(InputStream resourceStream = loader.getResourceAsStream("application.properties")){
properties.load(resourceStream);
}catch(IOException e){
e.printStackTrace();
}
return properties.entrySet();
}
I use gradle which structures projects in maven style so I have the following
src/main/java/Hello.java and src/main/resources/test.properties
My Hello.java look like this
public class Hello {
public static void main(String[] args) {
Properties configProperties = new Properties();
ClassLoader classLoader = Hello.class.getClassLoader();
try {
configProperties.load(classLoader.getResourceAsStream("test.properties"));
System.out.println(configProperties.getProperty("first") + " " + configProperties.getProperty("last"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
This works fine. however I want to be able to point to .properties file outside of my project and I want to it to be flexible enough that I can point to any location without rebuilding the jar every time. Is there a way to this without using a File API and passing file path as an argument to the main method?
You can try this one, which will first try to load properties file from project home directory so that you don't have to rebuild jar, if not found then will load from classpath
public class Hello {
public static void main(String[] args) {
String configPath = "test.properties";
if (args.length > 0) {
configPath = args[0];
} else if (System.getenv("CONFIG_TEST") != null) {
configPath = System.getenv("CONFIG_TEST");
}
File file = new File(configPath);
try (InputStream input = file.exists() ? new FileInputStream(file) : Hello.class.getClassLoader().getResourceAsStream(configPath)) {
Properties configProperties = new Properties();
configProperties.load(input);
System.out.println(configProperties.getProperty("first") + " " + configProperties.getProperty("last"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
You can send the properties file path as argument or set the path to an environment variable name CONFIG_TEST
Archaius may be complete overkill for such a simple problem, but it is a great way to manage external properties. It is a library for handling configuration: hierarchies of configuration, configuration from property files, configuration from databases, configuration from user defined sources. It may seem complicated, but you will never have to worry about hand-rolling a half-broken solution to configuration again. The Getting Started page has a section on using a local file as the configuration source.
I'm trying to configure the Java Logging API's FileHandler to log my server to a file within a folder in my home directory, but I don't want to have to create those directories on every machine it's running.
For example in the logging.properties file I specify:
java.util.logging.FileHandler
java.util.logging.FileHandler.pattern=%h/app-logs/MyApplication/MyApplication_%u-%g.log
This would allow me to collect logs in my home directory (%h) for MyApplication and would rotate them (using the %u, and %g variables).
Log4j supports this when I specify in my log4j.properties:
log4j.appender.rolling.File=${user.home}/app-logs/MyApplication-log4j/MyApplication.log
It looks like there is a bug against the Logging FileHandler:
Bug 6244047: impossible to specify driectorys to logging FileHandler unless they exist
It sounds like they don't plan on fixing it or exposing any properties to work around the issue (beyond having your application parse the logging.properties or hard code the path needed):
It looks like the
java.util.logging.FileHandler does not
expect that the specified directory
may not exist. Normally, it has to
check this condition anyway. Also, it
has to check the directory writing
permissions as well. Another question
is what to do if one of these check
does not pass.
One possibility is to create the
missing directories in the path if the
user has proper permissions. Another
is to throw an IOException with a
clear message what is wrong. The
latter approach looks more consistent.
It seems like log4j version 1.2.15 does it.
Here is the snippet of the code which does it
public
synchronized
void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
throws IOException {
LogLog.debug("setFile called: "+fileName+", "+append);
// It does not make sense to have immediate flush and bufferedIO.
if(bufferedIO) {
setImmediateFlush(false);
}
reset();
FileOutputStream ostream = null;
try {
//
// attempt to create file
//
ostream = new FileOutputStream(fileName, append);
} catch(FileNotFoundException ex) {
//
// if parent directory does not exist then
// attempt to create it and try to create file
// see bug 9150
//
String parentName = new File(fileName).getParent();
if (parentName != null) {
File parentDir = new File(parentName);
if(!parentDir.exists() && parentDir.mkdirs()) {
ostream = new FileOutputStream(fileName, append);
} else {
throw ex;
}
} else {
throw ex;
}
}
Writer fw = createWriter(ostream);
if(bufferedIO) {
fw = new BufferedWriter(fw, bufferSize);
}
this.setQWForFiles(fw);
this.fileName = fileName;
this.fileAppend = append;
this.bufferedIO = bufferedIO;
this.bufferSize = bufferSize;
writeHeader();
LogLog.debug("setFile ended");
}
This piece of code is from FileAppender, RollingFileAppender extends FileAppender.
Here it is not checking whether we have permission to create the parent folders, but if the parent folders is not existing then it will try to create the parent folders.
EDITED
If you want some additional functionalily, you can always extend RollingFileAppender and override the setFile() method.
You can write something like this.
package org.log;
import java.io.IOException;
import org.apache.log4j.RollingFileAppender;
public class MyRollingFileAppender extends RollingFileAppender {
#Override
public synchronized void setFile(String fileName, boolean append,
boolean bufferedIO, int bufferSize) throws IOException {
//Your logic goes here
super.setFile(fileName, append, bufferedIO, bufferSize);
}
}
Then in your configuration
log4j.appender.fileAppender=org.log.MyRollingFileAppender
This works perfectly for me.
To work around the limitations of the Java Logging framework, and the unresolved bug: Bug 6244047: impossible to specify driectorys to logging FileHandler unless they exist
I've come up with 2 approaches (although only the first approach will actually work), both require your static void main() method for your app to initialize the logging system.
e.g.
public static void main(String[] args) {
initLogging();
...
}
The first approach hard-codes the log directories you expect to exist and creates them if they don't exist.
private static void initLogging() {
try {
//Create logging.properties specified directory for logging in home directory
//TODO: If they ever fix this bug (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6244047) in the Java Logging API we wouldn't need this hack
File homeLoggingDir = new File (System.getProperty("user.home")+"/webwars-logs/weblings-gameplatform/");
if (!homeLoggingDir.exists() ) {
homeLoggingDir.mkdirs();
logger.info("Creating missing logging directory: " + homeLoggingDir);
}
} catch(Exception e) {
e.printStackTrace();
}
try {
logger.info("[GamePlatform] : Starting...");
} catch (Exception exc) {
exc.printStackTrace();
}
}
The second approach could catch the IOException and create the directories listed in the exception, the problem with this approach is that the Logging framework has already failed to create the FileHandler so catching and resolving the error still leaves the logging system in a bad state.
As a possible solution I think there are 2 approaches (look at some of the previous answers). I can extend a Java Logging Handler class and write my own custom handler. I could also copy the log4j functionality and adapt it to the Java Logging framework.
Here's an example of copying the basic FileHandler and creating a CustomFileHandler see pastebin for full class:
The key is the openFiles() method where it tries to create a FileOutputStream and checking and creating the parent directory if it doesn't exist (I also had to copy package protected LogManager methods, why did they even make those package protected anyways):
// Private method to open the set of output files, based on the
// configured instance variables.
private void openFiles() throws IOException {
LogManager manager = LogManager.getLogManager();
...
// Create a lock file. This grants us exclusive access
// to our set of output files, as long as we are alive.
int unique = -1;
for (;;) {
unique++;
if (unique > MAX_LOCKS) {
throw new IOException("Couldn't get lock for " + pattern);
}
// Generate a lock file name from the "unique" int.
lockFileName = generate(pattern, 0, unique).toString() + ".lck";
// Now try to lock that filename.
// Because some systems (e.g. Solaris) can only do file locks
// between processes (and not within a process), we first check
// if we ourself already have the file locked.
synchronized (locks) {
if (locks.get(lockFileName) != null) {
// We already own this lock, for a different FileHandler
// object. Try again.
continue;
}
FileChannel fc;
try {
File lockFile = new File(lockFileName);
if (lockFile.getParent() != null) {
File lockParentDir = new File(lockFile.getParent());
// create the log dir if it does not exist
if (!lockParentDir.exists()) {
lockParentDir.mkdirs();
}
}
lockStream = new FileOutputStream(lockFileName);
fc = lockStream.getChannel();
} catch (IOException ix) {
// We got an IOException while trying to open the file.
// Try the next file.
continue;
}
try {
FileLock fl = fc.tryLock();
if (fl == null) {
// We failed to get the lock. Try next file.
continue;
}
// We got the lock OK.
} catch (IOException ix) {
// We got an IOException while trying to get the lock.
// This normally indicates that locking is not supported
// on the target directory. We have to proceed without
// getting a lock. Drop through.
}
// We got the lock. Remember it.
locks.put(lockFileName, lockFileName);
break;
}
}
...
}
I generally try to avoid static code but to work around this limitaton here is my approach that worked on my project just now.
I subclassed java.util.logging.FileHandler and implemented all constructors with their super calls. I put a static block of code in the class that creates the folders for my app in the user.home folder if they don't exist.
In my logging properties file I replaced java.util.logging.FileHandler with my new class.