I want to save data with jackson to existing file (update it) but it won't work when I run my project from jar.
I need to use json as "database" (I know it's pretty stupid but that's for a school project) and to do it I load and save all the data when I do any of CRUD operations. It's working fine when I run it with IDE but when I tried as a jar it had a problem with reading file from ClassPathResource.
So I have this method to save changes to file:
private List<Item> items;
private ObjectMapper mapper;
private ObjectWriter writer;
public void saveData() {
mapper = new ObjectMapper();
writer = mapper.writer(new DefaultPrettyPrinter());
try {
writer.writeValue(new ClassPathResource("items.json").getFile(), items);
} catch (IOException e) {
e.printStackTrace();
}
}
And it works just fine when i run this through IntelliJ but it won't work when I run it as a jar.
I found a solution to loading the data by using InputStream from this question and method looks like this:
public void loadData() {
mapper = new ObjectMapper();
try {
ClassPathResource classPathResource = new ClassPathResource("items.json");
InputStream inputStream = classPathResource.getInputStream();
File tempFile = File.createTempFile("test", ".json");
FileUtils.copyInputStreamToFile(inputStream, tempFile);
System.out.println(tempFile);
System.out.println(ItemDao.class.getProtectionDomain().getCodeSource().getLocation().getPath().toString());
items = mapper.readValue(tempFile, new TypeReference<List<Item>>() {
});
} catch (IOException e) {
items = null;
e.printStackTrace();
}
}
But I still have no idea how to actually save changes. I was thinking about making use of FileOutputStreambut I achieved nothing.
So I want to get this working in jar file and be able to save changes to the same file, thanks for help in advance!
when you want to do read/write operations, it is better keep the file outside of the project. when running the jar, pass file name with path as an argument. like -DfileName=/Users/chappa/Documents/items.json etc. This way, you have absolute path, and you can perform read/write operations on it
if you are using java 1.7 or above, use below approach to write data.
To read data, you can use jackson api to load the json file as is.
Path wipPath = Paths.get("/Users/chappa/Documents/items.json");
try (BufferedWriter writer = Files.newBufferedWriter(wipPath)) {
for (String record : nosRecords) {
writer.write(record);
}
}
Just in case if you want to read json using IO streams, you can use below code
Path wipPath = Paths.get("/Users/chappa/Documents/items.json");
try (BufferedReader reader = Files.newBufferedReader(wipPath)) {
String line=null;
while((line = reader.readLine()) != null) {
System.out.println(line);
}
}
Related
I need to create a REST api that saves the names in a .txt file and that I can retrieve them when I needed. Below I have the code. I'm using the spring boot framework.
My REST controller:
#RequestMapping(value = "/people", method = RequestMethod.POST)
public #ResponseBody ResponseEntity<String> escreverArquivo() throws Exception {
String line = "";
try {
File file = new File("nomes.txt");
// Se o arquivo nao existir, ele gera
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file.getAbsoluteFile(), true);
BufferedWriter bw = new BufferedWriter(fw);
// Escreve e fecha arquivo
bw.write(line);
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
return new ResponseEntity<String>(line, HttpStatus.CREATED);
}
#RequestMapping(value = "/pessoas", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> lerArquivo() throws Exception {
String line = null;
try {
FileReader ler = new FileReader("nomes.txt");
BufferedReader reader = new BufferedReader(ler);
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return new ResponseEntity<String>(line, HttpStatus.OK);
}
The first method does not add anything that is not within the string line, the second method does not show anything on the screen, only in the sts console.
You should not be using a text file for storing any data you want to keep for any amount of time. They are not a reliable way of storing data, particularly as the load on your api increases.
You mention in a comment that your test requires a text file, but you should consider whether this is a good idea or not. Your tests should test the behaviour of your code, not the implementation. The tests should be happy with any code that makes saveName() and getNames() (or equivalent functions) behave as expected.
It is really worth getting comfortable with the basics of Java first before trying to use something like Spring Boot.
However, if you are really desperate to save a string to a text file, the Apache Commons IO library provides several nice helper methods, most notably:
FileUtils.write(new File("myFile.txt"), "Content Text", "UTF-8");
However, I strongly urge you to consider whether you really want a text file that is unencrypted, non-reduntant, easily corruptible, and likely not backed-up to store important data.
You should capture the POST data and initialize the line String with it. Something like
#RequestMapping(value = "/people", method = RequestMethod.POST)
#ResponseBody
public String escreverArquivo(#RequestParam(value="nomeDaPessoa") String nomeDaPessoa) throws Exception {
String line = nomeDaPessoa;
// ...
}
Note that you should replace nomeDaPessoa with the name of the field coming from your Form.
I'm creating a feature file programmatically using the following java code.
// This method will be invoked by 'constructFeatureFile()'
public static void createFeatureFile(String strFeatureFilePath, String strFeatureFileContent, String runnableFile)
{
try
{
if (strFeatureFilePath.trim().length() > 0 && strFeatureFileContent.trim().length() > 0)
{
// First time file creation
if (!Files.exists(Paths.get(strFeatureFilePath)))
{
FileWriter fw = new FileWriter(new File(strFeatureFilePath));
Writer wr = new BufferedWriter(fw);
wr.write(strFeatureFileContent);
wr.close();
} //If the exists, delete that file and create with fresh data
else if(Files.exists(Paths.get(strFeatureFilePath)))
{
Files.delete(Paths.get(strFeatureFilePath));
FileWriter fw = new FileWriter(new File(strFeatureFilePath));
Writer wr = new BufferedWriter(fw);
wr.write(strFeatureFileContent);
wr.close();
}
// Call Runner to run feature file
runner(runnableFile);
}
}
catch(Exception e)
{
System.out.print(e);
}
}
// Invoke feature file to execute
public static void runner(String runnableFile)
{
Map<String, Object> args = new HashMap<String,Object>();
args.put("name", "API Testing");
Map<String, Object> getResponse = CucumberRunner.runClasspathFeature(runnableFile, args, true);
getResponse = null;
}
The problem is, after creating the feature file, I'm calling Runner() method to execute the feature file. Its throwing the following exception.
java.lang.RuntimeException: file not found: country//getMembersDetails.feature
But, after refresing the folder (where the feature file was created), and re-run the same script, it's executing perfectly with the same feature file.
I don't understand why this is happening, what is the mistake I did here, anyone please provide suggestions.
Thanks!
It looks like createFeatureFile() is busy writing to strFeatureFilePath, but then calling runner(runnableFile). nothing was written to runnableFile?
I'm trying to make a runnable jar file and I'm having problems with my .txt files.
My program also have images, but fortunately I've figured out how to manage them. I'm using something like this with them and it works just fine both Eclipse and the jar:
logoLabel.setIcon(new ImageIcon(getClass().getResource("/logo.png")));
My problem is when I've something like this in one of my classes:
try {
employeeList = (TreeSet<Employee>) ListManager.readFile("list/employeeList.txt");
} catch (ClassNotFoundException i) {
i.printStackTrace();
} catch (IOException i) {
i.printStackTrace();
}
And this in the class ListManager that I use to read my lists serialized in the .txt files:
public static Object readFile(String file) throws IOException, ClassNotFoundException {
ObjectInputStream is = new ObjectInputStream(new FileInputStream(file));
Object o = is.readObject();
is.close();
return o;
}
I also have a similar method to write in the files.
I've tried several combinations that I've found here:
How to include text files with Executable Jar
Creating Runnable Jar with external files included
Including a text file inside a jar file and reading it
I've also tried with slash, without slash, using openStream, not using openStream... But or I get a NullPointerException or it doesn't compile at all...
Maybe is something silly or maybe is a concept error that I've of how URL class works, I'm new to programming...
Thank you very much in advance for your advice!
EDIT:
It's me again... The answer Raniz gave was just what I needed and it worked perfect, but now my problem is with the method that I use to write in the files...
public static void writeFile(Object o, String file) throws IOException {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file));
os.writeObject(o);
os.close();
}
try {
ListManager.writeFile(employeeList.getEmployeeList(), "lists/employeeList.txt");
} catch (IOException i) {
i.printStackTrace();
}
Could you help me please? I don't know what I should use to replace FileOutputStream, because I think there is the problem again, am I right?
Thank you very much!
The problem is that you're trying to access a file inside of a JAR archive as a file in the file system (because that's what FileInputStream is for) and that won't work.
You can convert readFile to use an URL instead and let URL handle opening the stream for you:
public static Object readFile(URL url) throws IOException, ClassNotFoundException {
ObjectInputStream is = new ObjectInputStream(url.openStream());
Object o = is.readObject();
is.close();
return o;
}
You should also put your code in a try-statement since it currently doesn't close the streams if an IOException occurs:
public static Object readFile(URL url) throws IOException, ClassNotFoundException {
try(ObjectInputStream is = new ObjectInputStream(url.openStream())) {
Object o = is.readObject();
return o;
}
}
try {
employeeList = (TreeSet<Employee>) ListManager.readFile(getClass().getResource("/list/employeeList.txt"));
} catch (ClassNotFoundException i) {
i.printStackTrace();
} catch (IOException i) {
i.printStackTrace();
}
I also have a similar method to write in the files.
That won't work if the files are inside the JAR so you should probably consider having your files outside your JAR.
Yes, if you want to read resources from inside a jar file, you shouldn't use FileInputStream. Perhaps you should add a readResource method:
public static Object readResource(Class clazz, String resource)
throws IOException, ClassNotFoundException {
try (ObjectInputStream is =
new ObjectInputStream(clazz.getResourceAsStream(resource))) {
return is.readObject();
}
}
(I'd also suggest updating your readFile method to use a try-with-resources block - currently if there's an exception you won't close the stream...)
Note that when you say "I also have a similar method to write in the files" - you won't be able to easily write to a resource in the jar file.
I have a file with name foo.txt. This file contains some text. I want to achieve following functionality:
I launch program
write something to the file (for example add one row: new string in foo.txt)
I want to get ONLY NEW content of this file.
Can you clarify the best solution of this problem? Also I want resolve related issues: in case if I modify foo.txt I want to see diff.
The closest tool which I found in Java is WatchService but if I understood right this tool can only detect type of event happened on filesystem (create file or delete or modify).
Java Diff Utils is designed for that purpose.
final List<String> originalFileContents = new ArrayList<String>();
final String filePath = "C:/Users/BackSlash/Desktop/asd.txt";
FileListener fileListener = new FileListener() {
#Override
public void fileDeleted(FileChangeEvent paramFileChangeEvent)
throws Exception {
// use this to handle file deletion event
}
#Override
public void fileCreated(FileChangeEvent paramFileChangeEvent)
throws Exception {
// use this to handle file creation event
}
#Override
public void fileChanged(FileChangeEvent paramFileChangeEvent)
throws Exception {
System.out.println("File Changed");
//get new contents
List<String> newFileContents = new ArrayList<String> ();
getFileContents(filePath, newFileContents);
//get the diff between the two files
Patch patch = DiffUtils.diff(originalFileContents, newFileContents);
//get single changes in a list
List<Delta> deltas = patch.getDeltas();
//print the changes
for (Delta delta : deltas) {
System.out.println(delta);
}
}
};
DefaultFileMonitor monitor = new DefaultFileMonitor(fileListener);
try {
FileObject fileObject = VFS.getManager().resolveFile(filePath);
getFileContents(filePath, originalFileContents);
monitor.addFile(fileObject);
monitor.start();
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (FileNotFoundException e) {
//handle
e.printStackTrace();
} catch (IOException e) {
//handle
e.printStackTrace();
}
Where getFileContents is :
void getFileContents(String path, List<String> contents) throws FileNotFoundException, IOException {
contents.clear();
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path), "UTF-8"));
String line = null;
while ((line = reader.readLine()) != null) {
contents.add(line);
}
}
What I did:
I loaded the original file contents in a List<String>.
I used Apache Commons VFS to listen for file changes, using FileMonitor. You may ask, why? Because WatchService is only available starting from Java 7, while FileMonitor works with at least Java 5 (personal preference, if you prefer WatchService you can use it). note: Apache Commons VFS depends on Apache Commons Logging, you'll have to add both to your build path in order to make it work.
I created a FileListener, then I implemented the fileChanged method.
That method load new contents form the file, and uses Patch.diff to retrieve all differences, then prints them
I created a DefaultFileMonitor, which basically listens for changes to a file, and I added my file to it.
I started the monitor.
After the monitor is started, it will begin listening for file changes.
I'm writing a play 2.0 java application that allows users to upload files. Those files are stored on a third-party service I access using a Java library, the method I use in this API has the following signature:
void store(InputStream stream, String path, String contentType)
I've managed to make uploads working using the following simple controller:
public static Result uploadFile(String path) {
MultipartFormData body = request().body().asMultipartFormData();
FilePart filePart = body.getFile("files[]");
InputStream is = new FileInputStream(filePart.getFile())
myApi.store(is,path,filePart.getContentType());
return ok();
}
My concern is that this solution is not efficient because by default the play framework stores all the data uploaded by the client in a temporary file on the server then calls my uploadFile() method in the controller.
In a traditional servlet application I would have written a servlet behaving this way:
myApi.store(request.getInputStream(), ...)
I have been searching everywhere and didn't find any solution. The closest example I found is Why makes calling error or done in a BodyParser's Iteratee the request hang in Play Framework 2.0? but I didn't found how to modify it to fit my needs.
Is there a way in play2 to achieve this behavior, i.e. having the data uploaded by the client to go "through" the web-application directly to another system ?
Thanks.
I've been able to stream data to my third-party API using the following Scala controller code:
def uploadFile() =
Action( parse.multipartFormData(myPartHandler) )
{
request => Ok("Done")
}
def myPartHandler: BodyParsers.parse.Multipart.PartHandler[MultipartFormData.FilePart[Result]] = {
parse.Multipart.handleFilePart {
case parse.Multipart.FileInfo(partName, filename, contentType) =>
//Still dirty: the path of the file is in the partName...
String path = partName;
//Set up the PipedOutputStream here, give the input stream to a worker thread
val pos:PipedOutputStream = new PipedOutputStream();
val pis:PipedInputStream = new PipedInputStream(pos);
val worker:UploadFileWorker = new UploadFileWorker(path,pis);
worker.contentType = contentType.get;
worker.start();
//Read content to the POS
Iteratee.fold[Array[Byte], PipedOutputStream](pos) { (os, data) =>
os.write(data)
os
}.mapDone { os =>
os.close()
Ok("upload done")
}
}
}
The UploadFileWorker is a really simple Java class that contains the call to the thrid-party API.
public class UploadFileWorker extends Thread {
String path;
PipedInputStream pis;
public String contentType = "";
public UploadFileWorker(String path, PipedInputStream pis) {
super();
this.path = path;
this.pis = pis;
}
public void run() {
try {
myApi.store(pis, path, contentType);
pis.close();
} catch (Exception ex) {
ex.printStackTrace();
try {pis.close();} catch (Exception ex2) {}
}
}
}
It's not completely perfect because I would have preferred to recover the path as a parameter to the Action but I haven't been able to do so. I thus have added a piece of javascript that updates the name of the input field (and thus the partName) and it does the trick.