I have a problem in a class I wrote. The purpose of the class is to add/remove/update for the applicationResource.properties files which the <spring:message code="key" /> uses to provide bilingual support to the website. Manually interacting with the properties files works fine, but I had a greater need and so I built in a way to allow changes to be done from the database. This has given me a very dynamic and flexible system that I can work from.
However, there is a problem. At some point after even a single change using this, the French characters end up getting changed. Such as Déconnexion becoming Déconnexion. When looked at in notepad++ its first Déconnexion and then corrupted to D\u00C3\u00A9connexion. This example was part of the original properties file.
The original (not temp) properties files have the text file encoding set to other: UTF-8. The Project properties text file encoding is set to inherited from container (Cp1252). I tried changing to Other: UTF-8 with no change.
So my question(s) is, what is causing the corruption to my French characters and how can I fix it? I have provided the complete class below.
Update: After the assistance from StephaneM in her answer I was able to track down exactly what is causing the corruption, but have not fixed it yet. The loadProperties() function in the AR Class. As soon as the temp AP files are loaded the French characters are corrupted. This makes me suspect the original process which creates the temp AP files is using a different encoding. So I will have to track it down.
package pojo;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
/*
* Purpose of this class is to handle all the ApplicationResource(_fr).properties interactions
* so that there is one unified location handling this, instead of code duplication.
*/
public class AR{
public final String en_path = "/ApplicationResources.properties";
public final String fr_path = "/ApplicationResources_fr.properties";
private Properties en_prop = null;
private Properties fr_prop = null;
public AR()
{
loadProperties();
}
private void loadProperties()
{
InputStream en_is = null;
InputStream fr_is = null;
try {
this.en_prop = new Properties();
this.fr_prop = new Properties();
en_is = this.getClass().getResourceAsStream(en_path);
fr_is = this.getClass().getResourceAsStream(fr_path);
en_prop.load(en_is);
fr_prop.load(fr_is);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean keyExist(String mykey, String mypath) //deprecated due to better code/method
{
Properties test_prop = null;
InputStream is = null;
try {
test_prop = new Properties();
is = this.getClass().getResourceAsStream(mypath);
test_prop.load(is);
Set<Object> keys = test_prop.keySet();
for(Object k:keys) {
String key = (String)k;
//System.out.print(key + " ");
if(key.equals(mykey))
{
return true;
}
}
//System.out.println(" ");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
public boolean en_keyExist(String mykey)
{
//searches english file
loadProperties();
return en_prop.containsKey(mykey);
//return keyExist(mykey, en_path); //original method
}
public boolean fr_keyExist(String mykey)
{
//searches french file
loadProperties();
return fr_prop.containsKey(mykey);
//return keyExist(mykey, fr_path); //original method
}
public boolean en_fr_keyExist(String mykey)
{
//searches both english and french files
loadProperties();
return (en_prop.containsKey(mykey) && fr_prop.containsKey(mykey));
//return (keyExist(mykey, en_path) && keyExist(mykey, fr_path)); //original method
}
public String en_returnProperty(String mykey)
{
//returns null if key does not exist
loadProperties();
return this.en_prop.getProperty(mykey);
}
public String fr_returnProperty(String mykey)
{
//returns null if key does not exist
loadProperties();
return this.fr_prop.getProperty(mykey);
}
public void appendProperty(Properties new_en_prop,Properties new_fr_prop)
{
//note: during a test, setProperty (used in populating the properties) does not allow duplicates, it overwrites.
//So, load the existing properties, and for each new property add it
loadProperties();
for(Object key : new_en_prop.keySet())
{
en_prop.setProperty((String)key, new_en_prop.getProperty((String)key));
}
try (OutputStream en_os = new FileOutputStream(getClass().getResource(en_path).getFile(),false);)
{
en_prop.store(en_os, null);
} catch (IOException e) {
e.printStackTrace();
}
for(Object key : new_fr_prop.keySet())
{
fr_prop.setProperty((String)key, new_fr_prop.getProperty((String)key));
}
try (OutputStream fr_os = new FileOutputStream(getClass().getResource(fr_path).getFile(),false);)
{
fr_prop.store(fr_os, null);
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean appendProperty(String mykey, String en_val, String fr_val) //appears to have timing error due to only saving last value
//due to timing error this function is only suitable for single additions
//due to the timing error, tried returning boolean to have it finished but was not successful
//setting the class variables to static did not solve the timing issue
{
loadProperties();
en_prop.setProperty(mykey, en_val);
try (OutputStream en_os = new FileOutputStream(getClass().getResource(en_path).getFile(),false);)
{
en_prop.store(en_os, null);
} catch (IOException e) {
e.printStackTrace();
}
fr_prop.setProperty(mykey, fr_val);
try (OutputStream fr_os = new FileOutputStream(getClass().getResource(fr_path).getFile(),false);)
{
fr_prop.store(fr_os, null);
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
public void en_setProperty(String mykey, String en_val)
//suspected timing issue, use only for singular changes
{
loadProperties();
en_prop.setProperty(mykey, en_val);
try (OutputStream en_os = new FileOutputStream(getClass().getResource(en_path).getFile(),false);)
{
en_prop.store(en_os, null);
} catch (IOException e) {
e.printStackTrace();
}
}
public void fr_setProperty(String mykey, String fr_val)
//suspected timing issue, use only for singular changes
{
loadProperties();
fr_prop.setProperty(mykey, fr_val);
try (OutputStream fr_os = new FileOutputStream(getClass().getResource(fr_path).getFile(),false);)
{
fr_prop.store(fr_os, null);
} catch (IOException e) {
e.printStackTrace();
}
}
public void compareResources()
{
Properties new_en = new Properties();
Properties new_fr = new Properties();
for(Object key : en_prop.keySet())
{
new_en.setProperty((String)key, en_prop.getProperty((String)key));
}
for(Object key : fr_prop.keySet())
{
new_fr.setProperty((String)key, fr_prop.getProperty((String)key));
}
Properties temp = (Properties) new_en.clone();
for(Object key : temp.keySet())
{
if(new_fr.containsKey((String) key))
{
new_fr.remove(key);
new_en.remove(key);
}
}
for(Object key : new_en.keySet())
{
System.out.println("English only key: " + ((String)key));
}
for(Object key : new_fr.keySet())
{
System.out.println("French only key: " + ((String)key));
}
}
}
Sample use case for the class, taken directly from application, but with some editing so only the relevant parts are here
AR testing = new AR();
Properties en_prop = new Properties();
Properties fr_prop = new Properties();
final String test_prod_cur = "{call BILINGUAL_VALUES(?)}";
ResultSet rs = null;
try (
Connection connection = jdbcTemplate.getDataSource().getConnection();
CallableStatement callableStatement = connection.prepareCall(test_prod_cur);
)
{
callableStatement.registerOutParameter(1, OracleTypes.CURSOR);
callableStatement.executeUpdate();
rs = (ResultSet) callableStatement.getObject(1);
while (rs.next())
{
String thead = rs.getString(1);
en_prop.setProperty(keyheader+thead, rs.getString(2));
fr_prop.setProperty(keyheader+thead, rs.getString(3));
//testing.appendProperty(keyheader+thead, rs.getString(2), rs.getString(3)); //has a timing issue, ends up only appending final value
}
}
catch (SQLException e)
{
System.out.println("SQLException - bilingual values");
System.out.println(e.getMessage());
}
testing.appendProperty(en_prop, fr_prop);
Regarding this question: "what is causing the corruption to my French characters and how can I fix it?", the answer is in the documentation ( Properties.store() ):
public void store(OutputStream out,
String comments)
throws IOException
Writes this property list (key and element pairs) in this Properties
table to the output stream in a format suitable for loading into a
Properties table using the load(InputStream) method.
Properties from the defaults table of this Properties table (if any)
are not written out by this method.
This method outputs the comments, properties keys and values in the
same format as specified in store(Writer), with the following
differences:
The stream is written using the ISO 8859-1 character encoding.
Characters not in Latin-1 in the comments are written as \uxxxx for their appropriate unicode hexadecimal value xxxx.
Characters less than \u0020 and characters greater than \u007E in property keys or values are written as \uxxxx for the appropriate hexadecimal value xxxx.
I am not the only person who has faced this issue, I managed to find another question and it was one of the answers that lead me to my solution. I have to thank another site for letting me know what to include.
There are only four added or changed lines, I will list them, and then give a complete function.
import java.io.Reader;
Reader reader = new InputStreamReader(fr_is, "UTF-8");
fr_prop.load(reader); //instead of fr_prop.load(fr_is);
reader.close();
The complete function
import java.io.Reader;
private void loadProperties()
{
InputStream en_is = null;
InputStream fr_is = null;
try {
this.en_prop = new Properties();
this.fr_prop = new Properties();
en_is = this.getClass().getResourceAsStream(en_path);
fr_is = this.getClass().getResourceAsStream(fr_path);
Reader reader = new InputStreamReader(fr_is, "UTF-8");
en_prop.load(en_is);
fr_prop.load(reader);
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
By introducing the reader and using that, it has cleared up French character corruption.
I should mention that I changed every file property I could find over to UTF-8 before I did the above changes and got it working. This site gives you the changes I made. This was a nice page that helped me confirm the encodings.
Related
I want to get one line as header and then rest of data append in the file.but i am facing issue that it is saving the header repeatedly when i have called the function.
Expected output should be like
Id : Title : Group ID
1 : ab : 2
2 : fd : 3
3 : fwsj : 3
public void writeOutputToFile(int id, String title, int groupId) throws IOException {
OutputStream os = new FileOutputStream(new File("output_report.txt"), true);
os.write("\n Id Title Group ID \n ".getBytes());
os.write((id + " " +title + " " + groupId + "\n").getBytes());
os.close();
}
well, inside your method you write the headers to the file, so obviously whenever you call it they'll get written..
You can separate it to two methods- one that writes the headers (and called only once) and another that writes the data (and called once per row).
Alternatively, use some sort of loop inside your method to write each of the lines to the file, after writing the headers once.
The Problem
It is repeatedly putting in the header, because when you call the method, you are always going to insert the header. Instead, you may want to code a util that inputs headers for a file you are creating, and then a separate method for inserting the data.
The Solution
Solution 1)
The helper util method would look something like this:
// String... allows for multiple string parameters to be entered for all of your headers.
public void prepFile(File f, String... headers) {
StringBuffer buffer = new StringBuffer();
for (String header : headers) {
buffer.append(header + "\t");
}
OutputStream os = new FileOutputStream(f, true);
os.write(buffer.toString().getBytes());
os.close();
}
After the file is prepped, you can then use your writeOutputToFile method for all the data.
Edit
Solution 2)
If you were going to make a stand alone class for this, I would recommend you set it up like so:
import java.io.*;
public class OutputFile {
private File file;
private String[] headers;
private boolean existed;
public OutputFile(File f, String... headers) {
this.file = f;
this.headers = headers;
init();
}
private void init() {
existed = file.exists();
// If the file didn't exist, then you want to create it.
if (!existed) {
try {
file.createNewFile();
// Afterwards, you can then write your headers to it.
if (headers != null) {
writeData(headers);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void writeData(int id, String title, int groupId) {
writeData("" + id, title, "" + groupId);
}
public void writeData(String... strings) {
StringBuffer buffer = new StringBuffer();
for (String s : strings) {
buffer.append(s + "\t");
}
buffer.append("\n");
writeData(buffer.toString());
}
public void writeData(String data) {
OutputStream os = null;
try {
os = new FileOutputStream(file, true);
os.write(data.getBytes());
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
I wrote a very simple Java web application ,just included some basic function like register , sign in , changing the password and some others.
I don't use database. I just create a file in the app to record the users' information and do the database stuff.
I used JMeter to stressing the web application, especially the register interface.
The JMeter shows that the result of the 1000 thread is right
but when I look into the information.txt , which stores the users' information, it's wrong because it stores 700+ record :
but it should include 1000 record, it must be somewhere wrong
I use the singleton class to do the write/read stuff, and i add a synchronized word to the class, the insert() function which is used by register to record the register information is shown as below: (a part of it)
public class Database {
private static Database database = null;
private static File file = null;
public synchronized static Database getInstance() {
if (database == null) {
database = new Database();
}
return database;
}
private Database() {
String path = this.getClass().getClassLoader().getResource("/")
.getPath() + "information.txt";
file = new File(path);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public void insert(String account, String password, String username) {
RandomAccessFile infoFile = null;
try {
infoFile = new RandomAccessFile(file, "rw");
String record;
long offset = 0;
while ((record = infoFile.readLine()) != null ) {
offset += record.getBytes().length+2;
}
infoFile.seek(offset);
record = account+"|"+password+"|"+username+"\r\n";
infoFile.write(record.getBytes());
infoFile.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (infoFile != null) {
try {
infoFile.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
}
the question is why would this happened , the synchronized is thread safe, why i lost so many data and some blank line was inserted into it, what could I do the correct it !
You are synchronizing the getInstance() method, but not the insert() method. This makes the retrieval of the instance of Database thread-safe, but not the write operation.
I wrote a code in java to run some scripts which can return different result depending on the environment setup. I would like to store the result of every execution. I try with properties file but every time it executes, it overwrites the previous result in config.properties. I did a research but not find any most likely example. This is my code to return properties file. The value which will be different are TCpassed and TCfailed on every execution.
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
public class ExecutionProperties {
public void setConfigProperties(int tcPassed, int tcFailed){
Properties prop = new Properties();
OutputStream output = null;
try {
output = new FileOutputStream("config.properties");
// set the properties value
prop.setProperty("TCpassed", ""+ tcPassed);
prop.setProperty("TCfailed", ""+ tcFailed);
// save properties to project root folder
prop.store(output, null);
} catch (IOException io) {
io.printStackTrace();
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Is it possible to get the execution time and store it in config.properties in order to differentiate with the previous result?
Thanks before
You can use append mode using constructor FileOutputStream("config.properties", true)
Sample properties file after couple of execution
#Mon May 04 13:03:29 IST 2015
TCpassed=1
TCfailed=1
#Mon May 04 13:04:03 IST 2015
TCpassed=1
TCfailed=1
Property file are usually key value pairs, e.g.
TCpassed=9
TCfailed=1
So if you want to store the result of every execution, you need a different key for every execution.
And if you want to append to the property file, you can:
Load the property file as Properties object;
Add new entry to the Properties object;
Write the Properties Object back to the file;
Here is an example:
public static void appendTestResult(File propertyFile, int tcPassed, int tcFailed) {
try {
Properties properties = loadProperties(propertyFile);
String testId = getTestID();
properties.setProperty("TCpassed_" + testId, String.valueOf(tcPassed));
properties.setProperty("TCfailed_" + testId, String.valueOf(tcFailed));
saveProperties(propertyFile, properties);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void saveProperties(File propertyFile, Properties properties) throws IOException {
OutputStream outputStream = null;
try {
outputStream = FileUtils.openOutputStream(propertyFile);
properties.store(outputStream, "new test");
} finally {
IOUtils.closeQuietly(outputStream);
}
}
public static Properties loadProperties(File propertyFile) throws IOException {
InputStream inputStream = null;
try {
inputStream = FileUtils.openInputStream(propertyFile);
Properties properties = new Properties();
properties.load(inputStream);
return properties;
} finally {
IOUtils.closeQuietly(inputStream);
}
}
public static String getTestID() {
return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
}
I have been asked to make a quiz and in the end of the quiz, what I want to keep are in two separate arrays names[] and scores[]. I have been asked to save the scores in a file using input and output. I have managed to do everything and successfully output the results and save it into the files (e.g. highscores.txt)
the results are saved like this:
4
name10
name5
name4
name2
the first line indicates how many scores there are in the file as every person who plays the quiz has their score saved.
What I am struggling to do is sort the scores to be placed in order of highest to lowest.
I have tried sorting the scores before they are saved in the file but the older scores in the file will not be sorted anymore. Example:
8
newname10
newname9
newname8
newname7
name10
name5
name4
name2
im only a beginner so go easy...
Well, because you don't use any kind of formatting to easily distinguish between username and score (eg: [username]:[score]) you've the problem to separate these two information from each other afterwards.
Anyhow - you need to load the data and reorder them trying to separate the username from the score. Read the file line my line and split the name from the score using regexp (in hope that the user name contains only letters): Pattern.compile("(\w+)(\d+)"). You can then use Matcher to get group 1 (the Name) and group 2 (the score). Then you can compare the score and reorder the List
What you should do is to create class Person which implements Comparable<Person> and if you are little bit lazy and the amount of data is not large Serializable too.
Then put everything inside TreeSet<Person> and write methods which serialize the TreeSet and deserialize it too.
Like this:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.TreeSet;
public class Person implements Comparable<Person>, Serializable {
private String name;
private int score;
public Person(String name, int score) {
this.name = name;
this.score = score;
addPerson(this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
#Override
public int compareTo(Person o) {
return score - o.score;
}
#Override
public String toString() {
return "Person [name=" + name + ", score=" + score + "]";
}
private static TreeSet<Person> classExtension = new TreeSet<Person>();
private static void addPerson(Person person) {
classExtension.add(person);
}
private static void removePerson(Person person) {
classExtension.remove(person);
}
private static void showExtension() {
for (Person person : classExtension)
System.out.println(person);
}
public static void serialize() {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream("Person.ser");
oos = new ObjectOutputStream(fos);
oos.writeObject(classExtension);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (oos != null)
oos.close();
} catch (IOException e) {
}
try {
if (fos != null)
fos.close();
} catch (IOException e) {
}
}
}
public static void deserialize(){
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream("Person.ser");
ois = new ObjectInputStream(fis);
classExtension = (TreeSet<Person>) ois.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
// zasoby zwalniamy w finally
try {
if (ois != null) ois.close();
} catch (IOException e) {}
try {
if (fis != null) fis.close();
} catch (IOException e) {}
}
}
}
If you're already able to sort the results before writing them to the file, why don't you read all the file before updating It? Then you can put the old data stored in your data structure with the new data,and sort them alla together. Then you should overwrite the old file with the data you already have.
This way could be expensive if your file has a lot of record.
The best way could be to use a collection that keep items in order. Take a look to PriorityQueue class. Then just store it into the file,and load It before adding new elements.
You can write the PriorityQueue to a file using an ObjectOutputStream and read it back with an ObjectInputStream.
This little program should do the Job.
Bear in mind that this really just sorts a text-file. If you need further processing according to your problem, modify it.
public class LineReader {
public static void main(String[] args) {
String file = args[0];
List<String> _list = new ArrayList<String>();
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
for (String line; (line = br.readLine()) != null;) {
_list.add(line);
}
} catch (Exception ex) {
}
java.util.Collections.sort(_list, String.CASE_INSENSITIVE_ORDER);
try(FileWriter writer = new FileWriter(file)) {
for (String str : _list) {
writer.write(str + "\n");
}
writer.close();
} catch (IOException ex) {
}
}
}
Okay, I'm trying to create a custom client for Minecraft (don't worry, my question has nothing to do with Minecraft in particular), and I added an abstract class to manage a configuration file using Java's built-in Properties system. I have a method that loads a properties file or creates it if it doesn't already exist. This method is called at the beginning of all my other methods (although it only does anything the first time its called).
The properties file gets created just fine when I run Minecraft the first time, but somehow when I run it the second time, the file gets blanked out. I'm not sure where or why or how I'm wiping the file clean, can someone please help me? Here's my code; the offending method is loadConfig():
package net.minecraft.src;
import java.util.*;
import java.util.regex.*;
import java.io.*;
/**
* Class for managing my custom client's properties
*
* #author oxguy3
*/
public abstract class OxProps
{
public static boolean configloaded = false;
private static Properties props = new Properties();
private static String[] usernames;
public static void loadConfig() {
System.out.println("loadConfig() called");
if (!configloaded) {
System.out.println("loading config for the first time");
File cfile = new File("oxconfig.properties");
boolean configisnew;
if (!cfile.exists()) {
System.out.println("cfile failed exists(), creating blank file");
try {
configisnew = cfile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
configisnew=true;
}
} else {
System.out.println("cfile passed exists(), proceding");
configisnew=false;
}
FileInputStream cin = null;
FileOutputStream cout = null;
try {
cin = new FileInputStream(cfile);
cout = new FileOutputStream(cfile);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (!configisnew) { //if the config already existed
System.out.println("config already existed");
try {
props.load(cin);
} catch (IOException e) {
e.printStackTrace();
}
} else { //if it doesn't exist, and therefore needs to be created
System.out.println("creating new config");
props.setProperty("names", "oxguy3, Player");
props.setProperty("cloak_url", "http://s3.amazonaws.com/MinecraftCloaks/akronman1.png");
try {
props.store(cout, "OXGUY3'S CUSTOM CLIENT\n\ncloak_url is the URL to get custom cloaks from\nnames are the usernames to give cloaks to\n");
cout.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
String names = props.getProperty("names");
System.out.println("names: "+names);
try {
usernames = Pattern.compile(", ").split(names);
} catch (NullPointerException npe) {
npe.printStackTrace();
}
System.out.println("usernames: "+Arrays.toString(usernames));
configloaded=true;
}
}
public static boolean checkUsername(String username) {
loadConfig();
System.out.println("Checking username...");
for (int i=0; i<usernames.length; i++) {
System.out.println("comparing "+username+" with config value "+usernames[i]);
if (username.startsWith(usernames[i])){
System.out.println("we got a match!");
return true;
}
}
System.out.println("no match found");
return false;
}
public static String getCloakUrl() {
loadConfig();
return props.getProperty("cloak_url", "http://s3.amazonaws.com/MinecraftCloaks/akronman1.png");
}
}
If it's too hard to read here, it's also on Pastebin: http://pastebin.com/9UscXWap
Thanks!
You are unconditionally creating new FileOutputStream(cfile). This will overwrite the existing file with an empty one. You should only invoke the FileOutputStream constructor when writing a new config file.
if (configloaded)
return;
File cfile = new File("oxconfig.properties");
try {
if (cfile.createNewFile()) {
try {
FileOutputStream cout = new FileOutputStream(cfile);
props.setProperty("names", "oxguy3, Player");
props.setProperty("cloak_url", "http://...");
...
cout.flush();
} finally {
cout.close();
}
} else {
FileInputStream cin = new FileInputStream(cfile);
try {
props.load(cin);
} finally {
cin.close();
}
}
configloaded=true;
} catch(IOException ex) {
e.printStackTrace();
}