BufferedWriter is not writing contents of Map to text file; Minecraft server - java

I have PVPStats objects stored in PlayerMeta.java:
public static Map <UUID, PVPstats> sPVPStats = new HashMap<>();
I know for sure the map is getting populated with objects that contain the expected vars for each uuid.
I'm trying to write these objects (converted to single lines of strings) into plugins/core/killstats.txt when the server calls onDisable() in Main.java
Along with the Map object, in PlayerMeta.java are also the methods to update and retrieve PVPStats objects from the Map. Those are all working.
The part that is not is working is the write method:
public static void writePVPStats() throws IOException {
BufferedWriter w = new BufferedWriter(new FileWriter("plugins/core/killstats.txt"));
sPVPStats.keySet().forEach(user -> {
try {
System.out.println(sPVPStats);
// stdout = {a6b6e3a1-a1ec-4fee-9d6d-f5e495c3e9d7=a6b6e3a1-a1ec-4fee-9d6d-f5e495c3e9d7:1:0}
w.write(user.toString() + "\n");
w.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
w.close();
}
kill.txt after onDisable() is done:
a6b6e3a1-a1ec-4fee-9d6d-f5e495c3e9d7
Instead it needs to be:
{a6b6e3a1-a1ec-4fee-9d6d-f5e495c3e9d7=a6b6e3a1-a1ec-4fee-9d6d-f5e495c3e9d7:1:0}
For reference, here is the complete PVPStats class.
Lastly, in case it matters / helps, the reader on server launch:
Files.readAllLines(killstats_user_database.toPath()).forEach(line -> {
PVPstats stats = PVPstats.fromString(line);
PlayerMeta.sPVPStats.put(stats.playerid, stats);
});
Source Code:
backend.FileManager.java
backend.PlayerMeta.java
backend.PVPstats.java
events.PVP.java
EDIT
I just tried this with killstats.txt file type nad killstats.txt doesnt have anything in it now.
public static void writePVPStats() throws IOException {
BufferedWriter w = new BufferedWriter(new FileWriter("plugins/core/killstats.txt"));
for (PVPstats object: sPVPStats.values()) {
try {
System.out.println(sPVPStats);
w.write(object.toString() + "\n");
w.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
w.close();
}

Ok so there were multiple issues. I wasn't controlling the flushing of the buffer, I declared the hasmap incorrectly, I wasn't accessing the value part of the hash map, and I wasn't correctly enforcing plain text.
SOLUTION
public static Map <UUID, PVPstats> sPVPStats = new HashMap<UUID, PVPstats>();
public static void writePVPStats() throws IOException {
BufferedWriter w = new BufferedWriter(new FileWriter("plugins/core/killstats.txt"));
for (PVPstats object: sPVPStats.values()) {
try {
System.out.println(sPVPStats);
System.out.println(object.toString());
w.write(object.toString() + "\n");
w.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
w.close();
}

Related

Append to csv in for loop using filewriter

I am working on a debuging csv output inside an event driven java application. I define my filewriter like this on init.
public File csvFile;
public FileWriter fileWriter;
then I initialies them
this.csvFile = new File("c:\\missingitems.csv");
this.fileWriter = null;
try {
this.fileWriter = new FileWriter(this.csvFile);
StringBuilder line = new StringBuilder();
line.append("Date, ItemId");
line.append("\n");
this.fileWriter.write(line.toString());
} catch (IOException e) {
e.printStackTrace();
}
then within my actual program logic this gets called for every timestep in my data
for(Long item : this.items) {
StringBuilder line = new StringBuilder();
line.append(event.getDateTime().toLocalDate().toString() + "," +item.intValue());
line.append("\n");
try {
this.fileWriter.write(line.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
on exit of my program I call
try {
this.fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
However this seems to be only working if i close the filewriter after each append. Is there a way of keeping the file open and just append to it, I would also like to not loose my data in case my application crashes. I am a python guy and not super familiar with java.

OpenCSV all of the data storing in single line version (5.1) and data loss

Hi while saving object by using openCsv the data getting stored in a single line and if i try to add more than one object the data getting deleted of the previous object. please if anyone knows the solution answer it here.
public final Logger log=Logger.getLogger("Error");
public void beanToCsvExport(final T object, final String filePath) {
if(object == null) {
log.warning("Initialization of object failed");
}else {
try {
Writer writer = new FileWriter(filePath);
StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder(writer).build();
beanToCsv.write(object);
writer.close();
} catch (IOException e) {
System.out.println(e.getMessage());
} catch (CsvDataTypeMismatchException e) {
System.out.println(e.getMessage());
} catch (CsvRequiredFieldEmptyException e) {
System.out.println(e.getMessage());
}
}
}
When you try to write more than one object the last one is overwritten because the FileWriter you are passing to StatefulBeanToCsv is not in append mode. You should specify the append mode in the FileWriter constructor.
Writer writer = new FileWriter(filePath, true);
StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder(writer).build();
beanToCsv.write(object);
writer.close();
Edit: This should solve multiple columns headers and column header not breaking line. It works for me.
public <T> void beanToCsvExport(final List<T> object, final String filePath) {
if (object == null) {
System.out.println("Initialization of object failed");
} else {
try {
cleanFile(filePath);
FileWriter writer = new FileWriter(filePath, true);
StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder(writer).build();
beanToCsv.write(object);
writer.close();
} catch (IOException | CsvDataTypeMismatchException | CsvRequiredFieldEmptyException e) {
System.out.println(e.getMessage());
}
}
}
The function that resets the file:
public void cleanFile(String path) throws IOException {
FileWriter writer = new FileWriter(path); //Open as non-append to delete all data.
writer.write("");
writer.close();
}
To test the functions:
public void employeesToCsv() {
List<Employee> list = Arrays.asList(new Employee("101", "sunny"), new Employee("102", "Andrew"));
beanToCsvExport(list, "file.csv");
}
The achieved output:
"eId","eName"
"101","sunny"
"102","Andrew"

Refactoring ArrayList write to file

I'm not sure how to efficiently re-factor the below code.
In the code below, there are 2 methods which each write data to a file. They are both quite similar except for:
First method accepts a single object parameter and then calls an external method for the write
Second method accepts an ArrayList of the objects as a paramter and loops over the ArrayList to perform the writes, directly invoking the writer.
Common sense tells me that this can be re-factored into a single write method which these two methods would then call, but not sure how to achieve this.
public void updateAccount(Account account) {
String outputString = outputStringCreator(account);
writeOutputString(outputString);
}
public void updateAccounts(ArrayList<Account> accounts) {
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(filePath));
for(Account account : accounts) {
writer.write(outputStringCreator(account));
writer.newLine();
}
} catch (IOException e) {
System.out.println("IO issue: " + e.getMessage());
}
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
System.out.println("Couldn't close writer: " + e.getMessage());
}
}
}
public void writeOutputString(String outputString) {
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(filePath));
writer.write(outputString);
writer.newLine();
} catch (IOException e) {
System.out.println("IO issue: " + e.getMessage());
}
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
System.out.println("Couldn't close writer: " + e.getMessage());
}
}
}
You can easily simplify overloading methods where one takes single parameter and other takes collection like this:
void foo( ObjectType object ) {
//pack object into singleton list
foo( List.of( object ) );
}
void foo( List< ObjectType > objects ) {
//execute logic on collection
}
Your method that takes collection should be able to handle cases where list has one element. That way you can easily just pass singleton list from overloading method and write only single algorithm.

Creating a position file using ObjectOutputStream

I have a HashMap where i store last read time of multiple sources which i needs to be backed up to a file. The same hashmap is updated regularly and should be backed up every time.
I am using ObjectOutputStream for this, as the same object is updated i was doing a reset() on the ObjectOutputStream, so that the file is updated, but with this is see and for every writeObject() a new line is written to the file this should be because the object is appended to the file. My service is long running service, so i can't afford the object to be appended every time as that will cause the file to become huge.
Here is a snippet of my code
public void open() throws WCException {
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(bookmarkFile));
bookmarks = (HashMap<String, Long>) objectInputStream.readObject();
objectInputStream.close();
} catch (ClassNotFoundException | IOException e) {
}
try {
fileOutputStream = new FileOutputStream(bookmarkFile);
objectOutputStream = new ObjectOutputStream(fileOutputStream);
} catch (IOException e) {
throw new WCException("Bookmarker", e.getCause());
}
}
public void close() throws WCException {
try {
objectOutputStream.close();
fileOutputStream.close();
} catch (IOException e) {
throw new WCException("Bookmarker", e.getCause());
}
}
public synchronized void write() throws WCException {
try {
objectOutputStream.writeObject(bookmarks);
objectOutputStream.reset();
} catch (IOException e) {
throw new WCException("Bookmarker", e.getCause());
}
}
public synchronized void update(HashMap<String, Long> bookmark) {
for (Map.Entry<String, Long> entry : bookmark.entrySet()) {
if (!bookmarks.containsKey(entry.getKey()))
bookmarks.put(entry.getKey(), entry.getValue());
else {
long last = bookmarks.get(entry.getKey());
if (last < entry.getValue())
bookmarks.put(entry.getKey(), entry.getValue());
}
}
}
I want something with which there is always a simple object in the file, which is latest. I am even ok with going away from ObjectOutputStream.

Java abstract class "instance variables"

I don't know if my mind just fools me or this is really not working.
I need different type of Logging-classes so I created a abstract-class, the only definition that all classes will have the same is the way the writeToLog is handled:
public abstract class LoggerTemplate {
protected String filename ="log/";
protected File logfile;
protected FileWriter fw;
public void writeToLog(String message) {
if(fw != null) {
try {
message = new SimpleDateFormat("dd-MM-hh:mm").format(new Date()) + " " + message;
fw.write(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The concrete sub-classes will implement rest of the logic in their constructor, ie one of them:
public class JitterBufferLogger extends LoggerTemplate {
public JitterBufferLogger() {
super();
filename += new SimpleDateFormat("yyyyddMMhhmm'.log'").format(new Date());
if(!new File("log/").exists())
new File("log").mkdir();
logfile = new File(filename);
try {
logfile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try {
fw = new FileWriter(logfile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
But when I debug I can see that when calling the writeToLog for a specific logger, it jumps into the LoggerTemplate method, and therefore fw and logfile are null. So it's not working.
Isn't it supposed to work or do I just mess something a bit up and should go into weekend ;-)
It should work, it is normal, that the debugger stepped into the LoggerTemplate class upon entering the writeToLog() method. What is strange that the attributes in the base class have null values.
I have tested your code with the following short test program:
public class Test {
public static void main(String[] args) {
LoggerTemplate lt = new JitterBufferLogger();
lt.writeToLog("Hello");
}
}
After adding fw.flush() to the LoggerTemplate.writeToLog() method just after the fw.write() call, it worked for me, the log file had been created and it contained the log message.
Maybe the new File("log").mkdir() or some other calls throw an exception which you cannot see, because stderr had been redirected somewhere.
So what may be missing?
- filewriter flushing could have helped.
- I can't reproduce the null values with the original code, don't know what happened.
- but as everybody, including me, said: it should work and it does.
Why was nothing in the logfile?
- maybe the flush of fw was missing..
anyhow I wrapped it with a Printwriter:
public abstract class LoggerTemplate {
protected String filename ="log/";
protected File logfile;
protected PrintWriter pw;
public void writeToLog(String message) {
try {
pw = new PrintWriter(new FileWriter(logfile,true));
message = new SimpleDateFormat("dd-MM-hh:mm").format(new Date()) + " " + message + "\n";
pw.write(message);
pw.flush();
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
and now it's working like it should and was expected to be.
Note that the fw instantiation in the concrete sub-classes is not needed anymore.

Categories

Resources