File Output between multiple runtimes? - java

I am working on this project where the user enters some data which is written to an XML file.This part is working fine.
Now when the user runs the program he should be able to append to that file. Instead it creates a new file with just one entry!
A fileoutput stream is also not the solution.
Here is the code for serializing to XML
String medicine=medicfield.getText();
String doctor=dnamefield.getText();
int duration=Integer.parseInt(dodfield.getText());
int amount=Integer.parseInt(cyclefield.getText());
int inter=Integer.parseInt(intval.getText());
PrescripManager pm=new PrescripManager();
pm.setDcycle(amount);
pm.setDosage(duration);
pm.setInterval(inter);
pm.setmedName(medicine);
pm.setdocName(doctor);
try{
FileOutputStream file = new FileOutputStream("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(PrescripManager.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.marshal(pm, file);
}
catch(Exception ex)
{
erlbl.setText(ex.getMessage());
}
And the Class::
#XmlRootElement
public class PrescripManager {
private String medname,docname;
private int interval,dcycle,dosage;
private Date dt;
public String getmedName() {
return medname;
}
public void setmedName(String medname) {
this.medname = medname;
}
public String getdocName() {
return docname;
}
public void setdocName(String docname) {
this.docname = docname;
}
public int getInterval() {
return interval;
}
public void setInterval(int interval) {
this.interval = interval;
}
public int getDcycle() {
return dcycle;
}
public void setDcycle(int dcycle) {
this.dcycle = dcycle;
}
public int getDosage() {
return dosage;
}
public void setDosage(int dosage) {
this.dosage = dosage;
}
}

First of all, you are writing an XML file. You can not just append to an XML file, because that would mean you are writing after then closing top level tag, resulting in invalid XML file.
You have at least three choices:
read old file in, add to the actual data, then write entire XML-file back.
write multiple files, each a valid XML file, with sequence number or timestamp in file name.
do not use XML, use a format which can be appended to
As a side note, if you want to append to file, you can open it in append mode. That will make every write to it append (at least on Unix, when file is opened in append mode, and I presume it works the same in Windows).
How to open file in append mode in Java: http://docs.oracle.com/javase/6/docs/api/java/io/FileOutputStream.html#FileOutputStream(java.io.File, boolean)

You can use the "FileWriter" class which allows you to write at the end of a file. See
http://docs.oracle.com/javase/6/docs/api/java/io/FileWriter.html
This link should help you:
http://www.coderanch.com/t/276346//java/do-open-file-append-mode
I think it should work if you use
FilterWriter file = new FileWriter( "file.xml" , true );
instead of
FileOutputStream file = new FileOutputStream("file.xml");

You are using JAXB to process XML files, so it's better that you change your XML file format and Java class to support this.
You can add a new class as collections of PrescripManager class instances. Something like PrescripManagerList.
public class PrescripManagerList{
#XmlElementWrapper(name = "prescripManagers")
#XmlElement(name = "prescripManager")
private ArrayList<PrescripManager> prescripManagers;
}
When you running your code, try to read an existing XML file using JAXB unmarshaller to get a PrescripManagerList object, then add a new PrescripManager object to the ArrayList, then write the updated PrescripManagerList object to file using JAXB marshaller.

Related

Trying to convert a CSV file to JSON format in order to save it in mongodb (spring)

This is my first post in StackOverflow!
I have been trying to upload a CSV file, parse it, and create a json file from it.
I found this tutorial using Jackson parser: https://kalliphant.com/jackson-convert-csv-json-example/
(I picked that because of the speed of processing), but I found errors when I tried it and I did not find why am I getting those, I am using intellij IDEA actually, and I tried using reload project and download sources from maven, but it did not solve the issue.
I have been looking for this error on the internet but I did not find any relevant.
I put the exact same code from the tutorial and I am getting those errors:
Errors I got:
Thank you!
The errors you're getting appear to be related to bad imports at your class file. Now even if that was OK, what you're trying to do you still not work.
Firstly, your CSV file is missing a header (above country), secondly as with normal JSON serialization/deserialization you need to perform this action against an object (a simple pojo). In your case your attempting to do this using object which is wrong -- both syntactically as well as conceptually.
With the above on hand try the following. Modify your CSV file to look like so:
country population mortality
spain 13000000 10000
france 30000000 15000
united kingdom 40000000 22000
belgium 20000000 50000
us 25000000 30000
The try the following code:
public class CsvParser {
public static void main(String... args) {
CsvSchema schema = CsvSchema
.emptySchema()
.withHeader();
ObjectReader reader = new CsvMapper()
.readerFor(MortalityEntry.class)
.with(schema);
List<MortalityEntry> results = null;
try {
MappingIterator<MortalityEntry> iterator = reader.readValues(Paths.get("/tmp/input.csv").toFile());
results = iterator.readAll();
} catch (IOException e) {
e.printStackTrace();
}
ObjectMapper objectMapper = new ObjectMapper();
try {
objectMapper.writeValue(Paths.get("/tmp/output.json").toFile(), results);
} catch (IOException e) {
e.printStackTrace();
}
}
private static class MortalityEntry {
private String country;
public String getCountry() { return country; }
public void setCountry(String country) { this.country = country; }
private Integer population;
public Integer getPopulation() { return population; }
public void setPopulation(Integer population) { this.population = population; }
private Integer mortality;
public Integer getMortality() { return mortality; }
public void setMortality(Integer mortality) { this.mortality = mortality; }
}
}
As you can see I'm using a simple pojo MortalityEntry to deserialize (from CSV) and serialize (to JSON), letting Jackson do its magic.
This simple example should be enough to get you going.

Is there a class to hold a file (content and name)

I only want to be sure before creating it. Is there a class to "store" a file in java. In my case, I only need name and content (byte array?!).
Using Lombok it is as simple as this:
import lombok.Value;
#Value
public class Document {
private String fileName;
private byte[] content;
}
But I would prefer to use a "native" class if there is one.
Your own class is fine.
public class Document {
private String fileName;
private byte[] content;
public Document(String fileName) {
this.fileName = fileName;
this.content = Files.readAllBytes(Paths.get(fileName));
}
}
Usage:
Document d = new Document("/path/to/file");
You should definitely consider whether you actually need the file name later on in the code or not, as it may be enough for you to use the content without a wrapping class.
Also note Java's IO provides many different ways to get the contents of a file - you are not limited to byte arrays. See Files for a non-exhaustive list of file-related operations, including the Files.lines() method to get a Stream<String> of all lines in a (text) file.

How to read all lines in ItemReader and return lines and file address to ItemProcessor?

I defined a job flow in my batch Spring project and defined ItemReader, ItemProcessor, ItemWriter, etc.
My ItemReader as below code :
#Component
#StepScope
public class MyFileReader extends FlatFileItemReader<FileInfo> {
private String fileName;
public MyFileReader () {
}
#Value("#{jobParameters[fileName]}")
public void setFileName(final String fileName) {
this.fileName = fileName;
}
#Override
public void afterPropertiesSet() throws Exception {
Resource resource = new FileSystemResource(fileName);
setResource(resource);
setEncoding("UTF-8");
super.afterPropertiesSet();
}
}
and my file input format is:
111111,11111,111,111
222222,22222,222,222
I want to read all lines of file and return lines and file address to ItemProcessor, but FlatFileItemReader read line by line. How do I do it correctly? Is overriding doRead method and handle problem manually correct?
If I'm understanding the question, you want to read in all lines from a file, store that data in an object and then pass said object to the processor. One approach would be to read all lines from the file before the job starts using a Job Listener. As illustrated below, you could read all lines in, populate a Java object that represents the content of a single row, collect all of those objects (so if there were two rows you'd populate 2 beans), and then pass them to the processor one at a time (or potentially at the same time, if you wish). It would look something like this:
First you would create a listener.
public class MyJobListenerImpl implements JobExecutionListener {
private MyFileReader reader;
#Override
public void beforeJob(JobExecution jobExecution) {
reader.init();
}
#Override
public void afterJob(JobExecution jobExecution) {
// noop
}
// Injected
public void setReader(MyFileReader reader) {
this.reader = reader;
}
Next add an init method to your custom reader.
public void init() {
if(Files.exists(inputFileLocation)) {
List<String> inputData = null;
try {
inputData = Files.readAllLines(inputFileLocation, StandardCharsets.UTF_8);
} catch(IOException e) {
System.out.println("issue reading input file {}. Error message: {}", inputFileLocation, e);
throw new IllegalStateException("could not read the input file.");
}
try {
for(String fileItem : inputData) {
YourFileDataBean fileData = new YourFileDataBean();
yourFileDataBean.setField1(fileItem.split(",")[0].trim());
yourFileDataBean.setFiled2(fileItem.split(",")[1].trim());
yourFileDataBean.setField3(fileItem.split(",")[2].trim());
yourFileDataBean.setField4(fileItem.split(",")[3].trim());
myDeque.add(yourFileDataBean); // really up to you how you want to store your bean but you could add a Deque instance variable and store it there.
}
} catch(ArrayIndexOutOfBoundsException e) {
LOGGER.warn("ArrayIndexOutOfBoundsException due to data in input file.");
throw new IllegalStateException("Failure caused by init() method. Error reading in input file.");
}
} else {
LOGGER.warn("Input file {} does not exist.", inputFileLocation);
throw new IllegalStateException("Input file does not exist at file location " + inputFileLocation);
}
}
Make your read() (or MyFileReader()) method in your custom reader return the object populated by all the file lines read in. In this example I am implementing ItemReader rather than extending it as you have done, but you get the idea. And if you intend to return a single Java object that represents the entire file then there would be no need to store the object in a Deque or List.
#Override
public MyFileReader read() throws NonTransientResourceException {
return myDeque.poll();
}
Hope this helps.
As for returning the file address to the ItemProcessor. You could make this a field in YourFileDataBean and store inputFileLocation there, or save it to the execution context and access it that way. If you inject this file path into your reader, you could do the same in your processor assuming your reader plays no role in determining the file path (aka, it's predetermined).

Should i store file names in Java Enum or properties file

I want to store file names, which keep on changing as the new files get added. I am looking for a minimum change in server code later when there is a need to support a new 'file' The thought I have is to store them either in properties file or as Java enum, but still thinking which is a better approach.
I am using REST and having 'file type' in the URL.
Example rest url:
hostname/file-content/TYPE
where value of TYPE could be any of these: standardFileNames1,standardFileNames2,randomFileName1,randomFileName2
I have used TYPE to group the files, so as to minimize the change in url when a new file is added. Dont want to have file names in the URL due to security issues.
my thought goes like this:
having as ENUM:
public enum FileType
{
standardFileNames1("Afile_en", "Afile_jp"),
standardFileNames2("Bfile_en","Bfile_jp"),
randomFileName1("xyz"),
randomFileName2("abc"),
...
...
}
having as properties file:
standardFileNames1=Afile_en,Afile_jp
standardFileNames2=Bfile_en,Bfile_jp
randomFileName1=xyz
randomFileName2=abc
I know having this in properties will save build efforts on every change, but still want to know your views to figure out best solution with all considerations.
Thanks!
Akhilesh
I often use property file + enum combination. Here is an example:
public enum Constants {
PROP1,
PROP2;
private static final String PATH = "/constants.properties";
private static final Logger logger = LoggerFactory.getLogger(Constants.class);
private static Properties properties;
private String value;
private void init() {
if (properties == null) {
properties = new Properties();
try {
properties.load(Constants.class.getResourceAsStream(PATH));
}
catch (Exception e) {
logger.error("Unable to load " + PATH + " file from classpath.", e);
System.exit(1);
}
}
value = (String) properties.get(this.toString());
}
public String getValue() {
if (value == null) {
init();
}
return value;
}
}
Now you also need a property file (I ofter place it in src, so it is packaged into JAR), with properties just as you used in enum. For example:
constants.properties:
#This is property file...
PROP1=some text
PROP2=some other text
Now I very often use static import in classes where I want to use my constants:
import static com.some.package.Constants.*;
And an example usage
System.out.println(PROP1);
Source:http://stackoverflow.com/questions/4908973/java-property-file-as-enum
My suggestion is to keep in properties or config file and write a generic code to get the file list and parse in java. So that whenever a new file comes, there will be no change on server side rather you will add an entry to the properties or config file.

Properties.store() - suppress timestamp comment

Is it possible to force Properties not to add the date comment in front? I mean something like the first line here:
#Thu May 26 09:43:52 CEST 2011
main=pkg.ClientMain
args=myargs
I would like to get rid of it altogether. I need my config files to be diff-identical unless there is a meaningful change.
Guess not. This timestamp is printed in private method on Properties and there is no property to control that behaviour.
Only idea that comes to my mind: subclass Properties, overwrite store and copy/paste the content of the store0 method so that the date comment will not be printed.
Or - provide a custom BufferedWriter that prints all but the first line (which will fail if you add real comments, because custom comments are printed before the timestamp...)
Given the source code or Properties, no, it's not possible. BTW, since Properties is in fact a hash table and since its keys are thus not sorted, you can't rely on the properties to be always in the same order anyway.
I would use a custom algorithm to store the properties if I had this requirement. Use the source code of Properties as a starter.
Based on https://stackoverflow.com/a/6184414/242042 here is the implementation I have written that strips out the first line and sorts the keys.
public class CleanProperties extends Properties {
private static class StripFirstLineStream extends FilterOutputStream {
private boolean firstlineseen = false;
public StripFirstLineStream(final OutputStream out) {
super(out);
}
#Override
public void write(final int b) throws IOException {
if (firstlineseen) {
super.write(b);
} else if (b == '\n') {
firstlineseen = true;
}
}
}
private static final long serialVersionUID = 7567765340218227372L;
#Override
public synchronized Enumeration<Object> keys() {
return Collections.enumeration(new TreeSet<>(super.keySet()));
}
#Override
public void store(final OutputStream out, final String comments) throws IOException {
super.store(new StripFirstLineStream(out), null);
}
}
Cleaning looks like this
final Properties props = new CleanProperties();
try (final Reader inStream = Files.newBufferedReader(file, Charset.forName("ISO-8859-1"))) {
props.load(inStream);
} catch (final MalformedInputException mie) {
throw new IOException("Malformed on " + file, mie);
}
if (props.isEmpty()) {
Files.delete(file);
return;
}
try (final OutputStream os = Files.newOutputStream(file)) {
props.store(os, "");
}
if you try to modify in the give xxx.conf file it will be useful.
The write method used to skip the First line (#Thu May 26 09:43:52 CEST 2011) in the store method. The write method run till the end of the first line. after it will run normally.
public class CleanProperties extends Properties {
private static class StripFirstLineStream extends FilterOutputStream {
private boolean firstlineseen = false;
public StripFirstLineStream(final OutputStream out) {
super(out);
}
#Override
public void write(final int b) throws IOException {
if (firstlineseen) {
super.write(b);
} else if (b == '\n') {
// Used to go to next line if did use this line
// you will get the continues output from the give file
super.write('\n');
firstlineseen = true;
}
}
}
private static final long serialVersionUID = 7567765340218227372L;
#Override
public synchronized Enumeration<java.lang.Object> keys() {
return Collections.enumeration(new TreeSet<>(super.keySet()));
}
#Override
public void store(final OutputStream out, final String comments)
throws IOException {
super.store(new StripFirstLineStream(out), null);
}
}
Can you not just flag up in your application somewhere when a meaningful configuration change takes place and only write the file if that is set?
You might want to look into Commons Configuration which has a bit more flexibility when it comes to writing and reading things like properties files. In particular, it has methods which attempt to write the exact same properties file (including spacing, comments etc) as the existing properties file.
You can handle this question by following this Stack Overflow post to retain order:
Write in a standard order:
How can I write Java properties in a defined order?
Then write the properties to a string and remove the comments as needed. Finally write to a file.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
properties.store(baos,null);
String propertiesData = baos.toString(StandardCharsets.UTF_8.name());
propertiesData = propertiesData.replaceAll("^#.*(\r|\n)+",""); // remove all comments
FileUtils.writeStringToFile(fileTarget,propertiesData,StandardCharsets.UTF_8);
// you may want to validate the file is readable by reloading and doing tests to validate the expected number of keys matches
InputStream is = new FileInputStream(fileTarget);
Properties testResult = new Properties();
testResult.load(is);

Categories