GSON not saving new fields to file - java

I'm adding new fields to my data class, they're not saving to file. None of the fields are transient.
I'm loading the file with GSON then resaving it to try to save new fields. I've tried setting the fields in the constructor and just setting them on creation. Neither worked, I also tried saving the file in the traditional way using GSON, that didn't work either. Modifying already existing fields works and they save properly, but new fields are never created.
private void loadUserData(Player player) {
File userFile = new File(userFolder + File.separator + player.getUniqueId().toString() + ".json");
try {
if (!userFile.exists()) {
User user = new User(player.getUniqueId(), player.getName(), player.isOp() ? "§8(§9Manager§8) §9" : "§7", "");
FileUtils.writeStringToFile(userFile, new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(user), StandardCharsets.UTF_8.name());
userCache.put(player.getUniqueId(), user);
} else {
User user = new GsonBuilder().create().fromJson(new FileReader(userFile), User.class);
user.save();
if(!userCache.containsKey(player.getUniqueId())) {
userCache.put(player.getUniqueId(), user);
}
}
} catch(IOException e) {
e.printStackTrace();
}
}
public void save() {
File userFolder = new File(Core.getInstance().getModuleManager().getModuleInstance(SMPModule.class).getDataFolder() + File.separator + "users");
try {
FileUtils.writeStringToFile(new File( userFolder + File.separator + uuid.toString() + ".json"),
new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(this), StandardCharsets.UTF_8.name());
} catch (IOException e) {
e.printStackTrace();
}
}
Data Class:
package net.astreul.core.module.impl.SMP.user;
import com.google.common.collect.Lists;
import com.google.gson.GsonBuilder;
import lombok.AccessLevel;
import lombok.Setter;
import net.astreul.core.Core;
import net.astreul.core.module.impl.SMP.SMPModule;
import net.astreul.core.module.impl.SMP.cosmetic.CosmeticPackage;
import net.astreul.core.util.Format;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
#Setter
public class User {
#Setter(AccessLevel.NONE) private UUID uuid;
private String nickname;
private String prefix;
private String suffix;
private long lastLogin;
private List<CosmeticPackage> ownedCosmetics;
private List<String> ownedTags;
private String activeTag;
public User(UUID uuid, String nickname, String prefix, String suffix) {
this.uuid = uuid;
this.nickname = nickname;
this.prefix = prefix;
this.suffix = suffix;
lastLogin = System.currentTimeMillis();
ownedCosmetics = Lists.newArrayList();
ownedTags = Lists.newArrayList();
this.activeTag = "";
}
public void save() {
File userFolder = new File(Core.getInstance().getModuleManager().getModuleInstance(SMPModule.class).getDataFolder() + File.separator + "users");
try {
FileUtils.writeStringToFile(new File( userFolder + File.separator + uuid.toString() + ".json"),
new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(this));
} catch (IOException e) {
e.printStackTrace();
}
}
public File getFile() {
File userFolder = new File(Core.getInstance().getModuleManager().getModuleInstance(SMPModule.class).getDataFolder() + File.separator + "users");
return new File( userFolder + File.separator + uuid.toString() + ".json");
}
public String getNickname() {
return Format.color(nickname);
}
public String getPrefix() {
return Format.color(prefix);
}
public String getSuffix() {
return Format.color(suffix);
}
public long getLastLogin() {
return lastLogin;
}
public boolean hasPackage(CosmeticPackage cosmeticPackage) {
return ownedCosmetics.contains(cosmeticPackage);
}
public List<CosmeticPackage> getOwnedCosmetics() {
return ownedCosmetics;
}
public List<String> getOwnedTags() {
return ownedTags;
}
public String getActiveTag() {
return activeTag;
}
}
I expected it to save the new fields to file, but it didn't save anything at all. There was no error at all.

I think the problem here is related to how gson creates instances.
If you look at the source code of gson you'll find that it tries to create the object instances using one of 3 things. First it tries instance creators - entities that know how to create an object. You can register one yourself to create the user - check here
If there's none it'll check for default constructors, which you don't have. A default constructor is one that has no arguments.
The last step is a best effort to create the object and it uses Java's unsafe. As the name suggests this is unsafe to do and the reason is because it bypasses all constructors. In your case, it'll bypass any initialization you have put inside the constructor and will leave the fields that are not in the json as null.
If you don't change the fields externally, once you save the user object, the new fields are still null and hence not saved.
To fix this, you can provide an instance creator, or initialize the fields from another method or somehow provide a default constructor.

Related

parse Json data with array using java

i use gson to get parese json data.but i have some problems
here is my json url data https://api.kcg.gov.tw/api/service/get/2c1d9959-d038-4918-bae3-409680f8193a
you can see that the json data have structure.
here is my code first
package iii_project;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;
import java.util.ArrayList;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class GsonFetchNetworkJson2 {
public class ItemType {
int seq;
String 資料年度;
String 統計項目;
String 稅目別;
String 資料單位;
String 值;
// public String toString() {
// return Arrays.toString(seq);
// }
}
public class Item {
String isImage;
String success;
String id;
ItemType[] data;
#Override
public String toString() {
return "Item [isImage=" + isImage + ", success=" + success + ", data=" + Arrays.toString(data) + "]";
}
}
public static void main(String[] ignored) throws Exception {
// Type listType = new TypeToken<List<Item>>() {}.getType();
URL url = new URL("https://api.kcg.gov.tw/api/service/get/2c1d9959-d038-4918-bae3-409680f8193a");
InputStreamReader reader = new InputStreamReader(url.openStream());
Item dto = new Gson().fromJson(reader, Item.class);
System.out.println(dto);
// System.out.println(dto.isImage);
// System.out.println(dto.id);
// System.out.println(dto.success);
// System.out.println(dto.types);
// System.out.println(dto.data.toString());
// System.out.println(dto.toString());
// System.out.println(Arrays.toString(dto.date));
// Detail abd = new Gson().fromJson(reader, Detail.class);
// System.out.println(Arrays.toString(abd.值));
// System.out.println(abd.資料單位);
// System.out.println(Arrays.toString(dto.data));
}
}
but the result of
System.out.println(dto);
i haved override it but still can not work
i want the data like this:
here is the answer!!!!
package iii_project;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;
import java.util.ArrayList;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class GsonFetchNetworkJson2 {
public class ItemType {
private int seq;
private String 資料年度;
private String 統計項目;
private String 稅目別;
private String 資料單位;
private String 值;
//getter and setter
#Override
public String toString() {
return "ItemType [seq=" + seq + ", 資料年度=" + 資料年度 + ", 統計項目=" + 統計項目 + ", 稅目別=" + 稅目別 + ", 資料單位=" + 資料單位
+ ", 值=" + 值 + "]";
}
}
public class Item {
String isImage;
String success;
String id;
ItemType[] data;
#Override
public String toString() {
return "Item [isImage=" + isImage + ", success=" + success + ", id=" + id + ", data="
+ Arrays.toString(data) + "]";
}
}
public static void main(String[] ignored) throws Exception {
// Type listType = new TypeToken<List<Item>>() {}.getType();
URL url = new URL("https://api.kcg.gov.tw/api/service/get/2c1d9959-d038-4918-bae3-409680f8193a");
InputStreamReader reader = new InputStreamReader(url.openStream());
Item dto = new Gson().fromJson(reader, Item.class);
// System.out.println(dto);
// System.out.println(Arrays.toString(dto.data));
System.out.println(dto.isImage);
System.out.println(dto);
// for(ItemType element : dto.data) {
// System.out.println(element);
// }
// System.out.println(dto.data);
// System.out.println(dto.id);
// System.out.println(dto.success);
// System.out.println(dto.types);
// System.out.println(dto.data.toString());
// System.out.println(dto.toString());
// System.out.println(Arrays.toString(dto.date));
// Detail abd = new Gson().fromJson(reader, Detail.class);
// System.out.println(Arrays.toString(abd.值));
// System.out.println(abd.資料單位);
// System.out.println(Arrays.toString(dto.data));
}
}
Your dto.data is array, so your current output is the address of that array.
Replace your System.out.println(dto.data) by System.out.println(Arrays.toString(array)); to print your data array
Check out this to have more details: What's the simplest way to print a Java array?
You need to override the toString function in your Detail class, otherwise it will only print out its address for the object.
but still can not work
It works exactly as you've written it to!
I want the data like this (shows image of some JSON object)
You need to explicitly write the indentation in your toString function, then
For example
public String toString() {
StringBuilder sb = new StringBuilder("{\n");
sb.append(" \"isImage\":" + isImage).append(",\n");
sb.append(" \"data\":");
if (data.length == 0) {
// add empty array string "[]"
} else {
sb.append("[");
for (ItemType it : data) {
// create object string
// append to outer builder
// add a comma, but not for the very last object in the array
}
sb.append("]");
}
// TODO: close off remaining end brackets
return sb.toString();
}
And your ItemType class still needs it's own toString...
If you really just want to print indented JSON without much code, then don't try to print an Item class
Pretty-Print JSON in Java

Get controller name and java service path in controller advice

Hi All i have created a global exception handler in my spring boot app and writing the exception occurred in AWS cloudwatch below code working fine i am able to write the exception in cloudwatch but the challenge is i am unable to get the Restcontroller name and service path from where the the particular exception happened.
Sample java service
#GetMapping(value = "DynamoDb/deleteTable")
public String deleteTable(#RequestParam String TableName) throws InterruptedException {
Table table = dynamoDB.getTable(TableName);
try {
table.delete();
table.waitForDelete();
} catch (Exception e) {
throw e;
}
return "Success";
}
When ever exception occurred it control transferred to controlleradvice global exception handler
Here is my code
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient;
import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogStreamsRequest;
import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogStreamsResponse;
import software.amazon.awssdk.services.cloudwatchlogs.model.InputLogEvent;
import software.amazon.awssdk.services.cloudwatchlogs.model.PutLogEventsRequest;
import java.util.Arrays;
#ControllerAdvice
public class ExceptionControllerAdvice {
#ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> exceptionHandler(Exception ex) {
ErrorResponse error = new ErrorResponse();
error.setErrorCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
error.setMessage(ex.getMessage());
error.setController(ex.getMessage());
error.setService(ex.getMessage());
error.setTimestamp(System.currentTimeMillis());
PutLogEvents(error);
return new ResponseEntity<ErrorResponse>(error, HttpStatus.OK);
}
public static void PutLogEvents(ErrorResponse Er)
{
String regionId = "us-east-1";
String logGroupName = "xxxxxxx";
String logStreamName = "xxxxxxx";
CloudWatchLogsClient logsClient = CloudWatchLogsClient.builder().region(Region.of(regionId)).build();
// A sequence token is required to put a log event in an existing stream.
// Look up the stream to find its sequence token.
String sequenceToken = getNextSequenceToken(logsClient, logGroupName, logStreamName);
// Build a JSON log using the EmbeddedMetricFormat.
String message = "[{" +
" \"Timestamp\": " + Er.getTimestamp() + "," +
" \"ErrorCode\": " + Er.getErrorCode() + "," +
" \"ControllerName\": " + Er.getErrorCode() + "," +
" \"ServiceName\": " + Er.getErrorCode() + "," +
" \"ErrorMsg\": " + Er.getErrorCode() + "" +
"}]";
InputLogEvent inputLogEvent = InputLogEvent.builder()
.message(message)
.timestamp(Er.getTimestamp())
.build();
// Specify the request parameters.
PutLogEventsRequest putLogEventsRequest = PutLogEventsRequest.builder()
.logEvents(Arrays.asList(inputLogEvent))
.logGroupName(logGroupName)
.logStreamName(logStreamName)
// Sequence token is required so that the log can be written to the
// latest location in the stream.
.sequenceToken(sequenceToken)
.build();
logsClient.putLogEvents(putLogEventsRequest);
}
private static String getNextSequenceToken(CloudWatchLogsClient logsClient, String logGroupName, String logStreamName) {
DescribeLogStreamsRequest logStreamRequest = DescribeLogStreamsRequest.builder()
.logGroupName(logGroupName)
.logStreamNamePrefix(logStreamName)
.build();
DescribeLogStreamsResponse describeLogStreamsResponse = logsClient.describeLogStreams(logStreamRequest);
// Assume that a single stream is returned since a specific stream name was
// specified in the previous request.
return describeLogStreamsResponse.logStreams().get(0).uploadSequenceToken();
}
}
Errorresponse.class
public class ErrorResponse {
private int errorCode;
private String message;
private String Controller;
private String Service;
private String ProjectName;
private long Timestamp;
public ErrorResponse(int errorCode, String message, String controller, String service, String projectName, long timestamp) {
this.errorCode = errorCode;
this.message = message;
Controller = controller;
Service = service;
ProjectName = projectName;
Timestamp = timestamp;
}
public ErrorResponse() {
}
#Override
public String toString() {
return "ErrorResponse{" +
"errorCode=" + errorCode +
", message='" + message + '\'' +
", Controller='" + Controller + '\'' +
", Service='" + Service + '\'' +
", ProjectName='" + ProjectName + '\'' +
", Timestamp=" + Timestamp +
'}';
}
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getController() {
return Controller;
}
public void setController(String controller) {
Controller = controller;
}
public String getService() {
return Service;
}
public void setService(String service) {
Service = service;
}
public String getProjectName() {
return ProjectName;
}
public void setProjectName(String projectName) {
ProjectName = projectName;
}
public long getTimestamp() {
return Timestamp;
}
public void setTimestamp(long timestamp) {
Timestamp = timestamp;
}
}
Could any one please help me how can i get the Restcontroller name and service path in Global exception handler?
Hi All Thanks to all by using below code i am able to get the result as suggested by client. Hope this may help some one. Thanks
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.HandlerMethod;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient;
import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogStreamsRequest;
import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogStreamsResponse;
import software.amazon.awssdk.services.cloudwatchlogs.model.InputLogEvent;
import software.amazon.awssdk.services.cloudwatchlogs.model.PutLogEventsRequest;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Arrays;
#ControllerAdvice
public class ExceptionControllerAdvice {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
#Value("${application.name}")
private String applicationName;
#Value("${aws.logGroupName}")
private String logGroupName;
#Value("${aws.logStreamName}")
private String logStreamName;
#ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> exceptionHandler(Exception ex, HandlerMethod handlerMethod, HttpServletRequest request) throws JsonProcessingException {
Class ControllerName = handlerMethod.getMethod().getDeclaringClass();
String MethodName = handlerMethod.getMethod().getName();
ErrorResponse error = new ErrorResponse();
error.setErrorCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
error.setErrorMessage(ex.getMessage());
error.setControllerName(ControllerName.toString());
error.setServiceName(MethodName.toString());
error.setTimeStamp(sdf.format(System.currentTimeMillis()));
error.setProjectName(applicationName);
error.setServicePath(request.getRequestURL().toString());
PutLogEvents(error);
return new ResponseEntity<ErrorResponse>(error, HttpStatus.OK);
}
public void PutLogEvents(ErrorResponse Er) throws JsonProcessingException {
String regionId = "xxxxx";
String logGroupName = "xxxxx";
String logStreamName = "xxxxx";
CloudWatchLogsClient logsClient = CloudWatchLogsClient.builder().region(Region.of(regionId)).build();
// A sequence token is required to put a log event in an existing stream.
// Look up the stream to find its sequence token.
String sequenceToken = getNextSequenceToken(logsClient, logGroupName, logStreamName);
// Build a JSON log using the EmbeddedMetricFormat.
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(Er);
String message =json;
InputLogEvent inputLogEvent = InputLogEvent.builder()
.message(message)
.timestamp(System.currentTimeMillis())
.build();
// Specify the request parameters.
PutLogEventsRequest putLogEventsRequest = PutLogEventsRequest.builder()
.logEvents(Arrays.asList(inputLogEvent))
.logGroupName(logGroupName)
.logStreamName(logStreamName)
// Sequence token is required so that the log can be written to the
// latest location in the stream.
.sequenceToken(sequenceToken)
.build();
logsClient.putLogEvents(putLogEventsRequest);
}
private static String getNextSequenceToken(CloudWatchLogsClient logsClient, String logGroupName, String logStreamName) {
DescribeLogStreamsRequest logStreamRequest = DescribeLogStreamsRequest.builder()
.logGroupName(logGroupName)
.logStreamNamePrefix(logStreamName)
.build();
DescribeLogStreamsResponse describeLogStreamsResponse = logsClient.describeLogStreams(logStreamRequest);
// Assume that a single stream is returned since a specific stream name was
// specified in the previous request.
return describeLogStreamsResponse.logStreams().get(0).uploadSequenceToken();
}
}
Result should be like this
{
"errorMessage": "Table already exists: ProductFgh (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ResourceInUseException; Request ID: 6S14VS0E6ESUMG55DL937IC42JVV4KQNSO5AEMVJF66Q9ASUAAJG)",
"timeStamp": "2020-Jan-22 11:53:58",
"errorCode": 500,
"projectName": "DynamoDB",
"servicePath": "http://localhost:8090/DynamoDb/createTable",
"controllerName": "class com.example.DynamoDB.DynamoDBController",
"serviceName": "createExampleTable"
}
As of now i have achieved this through above code if any better approach is available let me know. Thanks to all
You can get the class name from which the exception was thrown as follows:
ex.getStackTrace()[0].getClassName();

How to read properties file in java, which has value in key-value pair

if properties file contains below type values how to read it directly into map
user={'name':'test','age':'23','place':'london'}.
Thanks in advance!
You can inject values into a Map from the properties file using the #Value annotation like this.
#Value("#{${user}}")
private Map<String, String> user;
Entry in your application.properties must be:
user = {"name":"test","age":"23","place":"London"}
test.properties file
name=test
age=23
place=london
code
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;
public class ReadPropertiesFile {
public static void main(String[] args) {
try {
File file = new File("test.properties");
FileInputStream fileInput = new FileInputStream(file);
Properties properties = new Properties();
properties.load(fileInput);
fileInput.close();
Enumeration enuKeys = properties.keys();
while (enuKeys.hasMoreElements()) {
String key = (String) enuKeys.nextElement();
String value = properties.getProperty(key);
System.out.println(key + ": " + value);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Hope, this would help.. :)
This will read a property file and put it in Properties object.
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class PropertiesConfig {
private Properties prop;
private PropertiesConfig() {
super();
init();
}
private static class PropertiesInstance {
private static PropertiesConfig instance = null;
public static PropertiesConfig getInstance() {
if (null == instance) {
instance = new PropertiesConfig();
}
return instance;
}
}
public static PropertiesConfig getInstance() {
return PropertiesInstance.getInstance();
}
private void init() {
prop = new Properties();
try (InputStream input = getClass().getResourceAsStream("/sample_config.properties")) {
// load a properties file
prop.load(input);
} catch (IOException ex) {
ex.printStackTrace();
}
}
public String getProperty(String key) {
return prop.getProperty(key);
}
}
Now you can use any library to convert a value to Map as your value looks like a JSON.
Code example to achieve this through Jackson:
public static Map < String, Object > covertFromJsonToMap(String json) throws JsonTransformException {
try {
return mapper.readValue(json, new TypeReference < HashMap < String, Object >> () {});
} catch (Exception e) {
LOGGER.error("Error " + json, e);
throw new JsonTransformException("Error in json parse", e);
}
}
So something like this will do:
covertFromJsonToMap(PropertiesConfig.getInstance().get("user"));
Looks like you have a JSON representation of your map as a value. Once you read the value as a String in Java you can use Gson to convert it to map
Gson gson = new Gson();
String json = <YOUR_STRING>
Map<String,Object> map = new HashMap<String,Object>();
map = (Map<String,Object>) gson.fromJson(json, map.getClass());

Java : InputStream to Multi-part file conversion, result file is empty

I am working on a Java application in which I am trying to create a Multipart file out of downloaded InputStream. Unfortunately, it is not working and the Multipart file is empty. I checked the size of savedFile on disk before copying it to Multipart, and it has correct size, attributes, content.
What am I doing wrong in the conversion, there is no stacktrace, as I am catching it.
Code :
// InputStream contains file data.
byte[] bytes = IOUtils.toByteArray(inputStream);
File file = new File(msg + "temp");
if (file.exists() && file.isDirectory()) {
OutputStream outputStream = new FileOutputStream(new File(msg + "temp" + "/" +
groupAttachments.getFileName()));
outputStream.write(bytes);
outputStream.close();
}
java.io.File savedFile = new java.io.File(msg + "temp" + "/" +
groupAttachments.getFileName());
DiskFileItem fileItem = new DiskFileItem("file", "text/plain", false,
savedFile.getName(), (int) savedFile.length(), savedFile.getParentFile());
fileItem.getOutputStream();
MultipartFile multipartFile = new CommonsMultipartFile(fileItem);
System.out.println("Saved file size is "+savedFile.length());
if (multipartFile.isEmpty()) {
System.out.println("Dropbox uploaded multipart file is empty");
} else {
System.out.println("Multipart file is not empty.");
}
this.dropboxTask.insertFile(multipartFile, "",
savedPersonalNoteObject.getNoteid(), (long) 0, true);
Path path = Paths.get(msg + "temp" + "/" + groupAttachments.getFileName());
Console output :
Multipart file is not empty
Bytes are not null
File path is /My Group
Input stream is not null
Saved file size is 4765
Dropbox uploaded multipart file is empty
Multipart file is empty
Bytes are not null
What am I doing wrong in the conversion? Any help would be nice. Thanks a lot.
The DiskFileItem uses a DeferredFileOutputStream which uses an in-memory byte-array that is only filled when bytes are actually transferred.
Since files are used directly and no bytes are actually copied,
the byte-array is never filled. See for yourself in the source code:
Source code CommonsMultipartFile
Source code DiskFileItem
Source code DeferredFileOutputStream
So, instead of just calling fileItem.getOutputStream();, transfer the bytes to fill the in-memory byte-array:
try (OutputStream out = fileItem.getOutputStream();
InputStream in = Files.newInputStream(file.toPath())) {
IOUtils.copy(in, dfos);
}
and then the tranferTo call will work.
This appears to be a bit cumbersome for just moving a file: CommonsMultipartFile only calls fileItem.write((File)dest) in the transferTo method.
Below are two test cases, one using the DiskFileItem and one using the LocalFileItem. The code for LocalFileItem is shown further below.
I used dependencies org.springframework:spring-web:4.2.2.RELEASE, commons-fileupload:commons-fileupload:1.3.1 and junit:junit:4.12
Test class CommonMp:
import static org.junit.Assert.*;
import java.io.*;
import java.nio.charset.*;
import java.nio.file.*;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
public class CommonMp {
private final Charset CS = StandardCharsets.UTF_8;
#Test
public void testLocalMp() {
Path testInputFile = null, testOutputFile = null;
try {
testInputFile = prepareInputFile();
LocalFileItem lfi = new LocalFileItem(testInputFile);
CommonsMultipartFile cmf = new CommonsMultipartFile(lfi);
System.out.println("Empty: " + cmf.isEmpty());
testOutputFile = testInputFile.getParent().resolve("testMpOutput.txt");
cmf.transferTo(testOutputFile.toFile());
System.out.println("Size: " + cmf.getSize());
printOutput(testOutputFile);
} catch (Exception e) {
e.printStackTrace();
fail();
} finally {
deleteSilent(testInputFile, testOutputFile);
}
}
#Test
public void testMp() {
Path testInputFile = null, testOutputFile = null;
try {
testInputFile = prepareInputFile();
DiskFileItem di = new DiskFileItem("file", "text/plain", false, testInputFile.getFileName().toString(),
(int) Files.size(testInputFile), testInputFile.getParent().toFile());
try (OutputStream out = di.getOutputStream();
InputStream in = Files.newInputStream(testInputFile)) {
IOUtils.copy(in, out);
}
CommonsMultipartFile cmf = new CommonsMultipartFile(di);
System.out.println("Size: " + cmf.getSize());
testOutputFile = testInputFile.getParent().resolve("testMpOutput.txt");
cmf.transferTo(testOutputFile.toFile());
printOutput(testOutputFile);
} catch (Exception e) {
e.printStackTrace();
fail();
} finally {
deleteSilent(testInputFile, testOutputFile);
}
}
private Path prepareInputFile() throws IOException {
Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir"));
Path testInputFile = tmpDir.resolve("testMpinput.txt");
try (OutputStream out = Files.newOutputStream(testInputFile)){
out.write("Just a test.".getBytes(CS));
}
return testInputFile;
}
private void printOutput(Path p) throws IOException {
byte[] outBytes = Files.readAllBytes(p);
System.out.println("Output: " + new String(outBytes, CS));
}
private void deleteSilent(Path... paths) {
for (Path p : paths) {
try { if (p != null) p.toFile().delete(); } catch (Exception ignored) {}
}
}
}
The custom LocalFileItem class, YMMV!
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemHeaders;
public class LocalFileItem implements FileItem {
private static final long serialVersionUID = 2467880290855097332L;
private final Path localFile;
public LocalFileItem(Path localFile) {
this.localFile = localFile;
}
#Override
public void write(File file) throws Exception {
Files.move(localFile, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
#Override
public long getSize() {
// Spring's CommonsMultipartFile caches the file size and uses it to determine availability.
long size = -1L;
try {
size = Files.size(localFile);
} catch (IOException ignored) {}
return size;
}
#Override
public void delete() {
localFile.toFile().delete();
}
/* *** properties and unsupported methods *** */
private FileItemHeaders headers;
private String contentType;
private String fieldName;
private boolean formField;
#Override
public FileItemHeaders getHeaders() {
return headers;
}
#Override
public void setHeaders(FileItemHeaders headers) {
this.headers = headers;
}
#Override
public InputStream getInputStream() throws IOException {
throw new IOException("Only method write(File) is supported.");
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
#Override
public String getContentType() {
return contentType;
}
#Override
public String getName() {
return localFile.getFileName().toString();
}
#Override
public boolean isInMemory() {
return false;
}
#Override
public byte[] get() {
throw new RuntimeException("Only method write(File) is supported.");
}
#Override
public String getString(String encoding)
throws UnsupportedEncodingException {
throw new RuntimeException("Only method write(File) is supported.");
}
#Override
public String getString() {
throw new RuntimeException("Only method write(File) is supported.");
}
#Override
public String getFieldName() {
return fieldName;
}
#Override
public void setFieldName(String name) {
this.fieldName = name;
}
#Override
public boolean isFormField() {
return formField;
}
#Override
public void setFormField(boolean state) {
this.formField = state;
}
#Override
public OutputStream getOutputStream() throws IOException {
throw new IOException("Only method write(File) is supported.");
}
}

IndexOutOfBoundsException in File array. What could be the issue?

So I have a directory in my local C drive.
C:/Search Files/Folder [number]/hello.txt
Inside Search Files I have four foldes named:
Folder 1
Folder 2
Folder 3
Folder 4
Inside Folder 1 I have a a file called hello.txt with some String in it.
What I want to do is grab the fileDirectory, fileName and fileContent and put it in a List of XMLMessage objects. I have pasted my main class and my XMLMessage POJO. When I run it, I am getting an indexOutOfBoundsException. I have been stuck for a couple hours now. I need another pair of eyes to look into this.
Thanks,
package org.raghav.stuff;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.io.FileUtils;
public class GetFile {
public static void main(String[] args) throws IOException {
File[] files = new File("C:\\Search Files").listFiles();
showFiles(files);
}
public static void showFiles(File[] files) throws IOException {
String line = null;
List<XMLMessage> xmlMessageList = new ArrayList<XMLMessage>();
int i = 0;
//XMLMessage folderFile = new XMLMessage();
try {
for (File file : files) {
if (file.isDirectory()) {
String fileName = file.getName();
System.out.print(fileName);
xmlMessageList.get(i).setFileName(fileName);
//folderFile.setFileName(fileName);
showFiles(file.listFiles()); // Calls same method again.
} else {
xmlMessageList.get(i).setFileDirectory(file.getName() + file.toString());
//folderFile.setFileDirectory(file.getName() + file.toString());
System.out.print("\tFile: " + file.getName()
+ file.toString());
// System.out.println("Directory: " + file.getName());
BufferedReader in = new BufferedReader(new FileReader(file));
while ((line = in.readLine()) != null) {
xmlMessageList.get(i).setFileContent(line);
// folderFile.setFileContent(line);
System.out.print("\t Content:" + line);
}
in.close();
System.out.println();
}
i++;
}
} catch (NullPointerException e) {
e.printStackTrace();
}
System.out.println(xmlMessageList.toString());
}
}
Here is the POJO:
package org.raghav.stuff;
public class XMLMessage {
private String fileDirectory;
private String fileName;
private String fileContent;
public final String FILE_NAME = "fileName";
public final String FILE_DIRECTORY = "fileDirectory";
public XMLMessage(String fileDirectory, String fileName, String fileContent) {
this.fileDirectory = fileDirectory;
this.fileName = fileName;
this.fileContent = fileContent;
}
public XMLMessage() {
}
public String getFileDirectory() {
return fileDirectory;
}
public void setFileDirectory(String fileDirectory) {
this.fileDirectory = fileDirectory;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileContent() {
return fileContent;
}
public void setFileContent(String fileContent) {
this.fileContent = fileContent;
}
public String toString(){
String returnString = "File Directory: " + fileDirectory + "\n" + "File Name" + fileName + "\n" + "File Content: " + fileContent;
return returnString;
}
/*public String createResponseFileName(String fileName){
int lastDot = fileName.lastIndexOf('.');
String responseFileName = fileName.substring(0, lastDot) + "Response" + fileName.substring(lastDot);
return responseFileName;
}*/
/*public String createResponseFileContent(String fileContent){
this.
}*/
}
You're never populating your list. I suspect you should actually have:
for (File file : files) {
XMLMessage message = new XMLMessage();
xmlMessageList.add(message);
if (file.isDirectory()) {
String fileName = file.getName();
System.out.print(fileName);
message.setFileName(fileName);
//folderFile.setFileName(fileName);
showFiles(file.listFiles()); // Calls same method again.
} else {
... etc, using message instead of xmlMessageList.get(i)
}
}
Then you don't need the i variable at all.
I think Jon Skeet is right.
you never populate your list.
you should use your constructor
XmlMessage m = new XMLMessage( fileDirectory, fileName,fileContent)
xmlMessageList.add(m);

Categories

Resources