My Android code is behaving funny. The input stream should and does throw an IOException, which correctly causes control to go to // read an error stream. The error stream is read correctly and the debugger steps to return error_message with the error_message variable containing expected characters read from error stream. It then correctly steps to the // no op in the finally block, which I added just for kicks.
And then, it steps to return "all hope lost";!! Which then, instead of returning to the caller, steps into some Android system code that throws a SecurityException with a message about lack of content permissions.
Removing the finally block has no impact -- the bug still happens. The streams being read are from an HTTP URL Connection. No problems if server returns 200 but if server returns 400 it goes through the weird path described above and tries to throw the weird SecurityException.
try {
// read an input stream into message
return message;
} catch (IOException outer) {
try {
// read an error stream into error_message
return error_message;
} catch (IOException inner) {
return "all hope lost";
}
} finally {
// no op, just to step debugger
}
Update: Posting exact code and debug trace.
try {
/*x*/ BufferedReader buffered_reader =
new BufferedReader(
new InputStreamReader(
new BufferedInputStream(http_url_connection.getInputStream())));
StringBuilder string_builder = new StringBuilder();
String line;
for (line = buffered_reader.readLine();
line != null;
line = buffered_reader.readLine()) {
string_builder.append(line);
}
return string_builder.toString();
} catch (IOException io_exception) {
this.io_exception = io_exception;
BufferedReader buffered_reader =
new BufferedReader(
new InputStreamReader(
new BufferedInputStream(http_url_connection.getErrorStream())));
StringBuilder string_builder = new StringBuilder();
try {
for (String line = buffered_reader.readLine();
line != null;
line = buffered_reader.readLine()) {
string_builder.append(line);
}
/*y*/ String error_message = "server error: " + string_builder.toString();
return error_message;
} catch (IOException exception) {
String level_2_error_message = "level 2 error: " + exception.getMessage();
return level_2_error_message;
} finally {
return "foo";
}
}
Line /*x*/ causes the jump to the first catch as expected. All lines up to /*y*/ are then executed as expected. Then the weird thing is that line /*y*/ does not complete and control immediately goes to either the next catch block if there is no finally or to the finally. If there is a finally then it does not to got the last catch block.
The contents of the string buffer on /*y*/ line look perfectly fine -- a 20 character string from the server.
You say that an exception is being thrown by line /* y */
By my reading of that line of code, the following are plausible explanations:
The exception is a NullPointerException because string_builder is null. But it can't be.
The exception is an OutOfMemoryError because you don't have enough free space for the toString() call to create the new String object.
It is possible that StringBuilder is not java.lang.StringBuilder but some class you wrote yourself. In that case, any exception is possible.
However, I can't see how you would end up in the second IOException handler.
Apart from that, the only other likely explanation is that that source code does not match the code that you are actually executing; e.g. you forgot to recompile something, or you forgot to redeploy after your last compilation.
For what it is worth, your return in your finally is almost certainly a mistake.
It means that you will return "foo" instead of either of the error messages.
If (for example) string_builder.toString() did throw an NPE or OOME, then the return would squash it.
A finally with a return can have non-intuitive behaviour. It is certainly NOT something you should do "for debugging"!!!
Related
I want to make a test that reads from a file some data and passes that data to a function. That function calls other methods and some of them throw some exceptions. I'm interested in how can I check whether or not calling the method with the parameters from the file triggered an IOException somewhere along. I know that the code snippet provided will stop the execution because I've used assert. How should I write if I want to check if an IOException was thrown and if it was, to get the error message, without stopping the execution of the test? Thanks!
void test() throws IOException {
Service service = helperFunction();
File articles = new File("file.txt");
Scanner scanner = new Scanner(articles);
while(scanner.hasNextLine()) {
String line = scanner.nextLine();
line = line.replaceAll("[^\\d]", " ");
line = line.trim();
line = line.replaceAll(" +", " ");
String[] numberOnTheLine = line.split(" ");
List<Integer> list = Arrays.stream(numberOnTheLine).map(Integer::valueOf).collect(Collectors.toList());
Article article = new Article(Long.valueOf(list.get(0)),
new HashSet<>(List.of(new Version(list.get(1)))));
List<List<Article>> listOfArticles = Collections.singletonList(List.of(article));
Assertions.assertThrows(IOException.class,
() -> service.etlArticles(listOfArticles.stream().flatMap(List::stream).collect(Collectors.toList())));
}
}
Simple; a try/catch statement will take care of it.
Replace this:
service.etlArticles(listOfArticles.stream().flatMap(List::stream).collect(Collectors.toList())));
With:
try {
service.etlArticles(listOfArticles.stream().flatMap(List::stream).collect(Collectors.toList())));
} catch (IOException e) {
// Code jumps to here if an IOException occurs during the execution of anything in the try block
}
You are free to e.g. do some logging and then just Assert.fail, if you want.
assertThrows is quite simple, all it does is this:
try {
runThatCode();
} catch (Throwable e) {
if (e instanceof TypeThatShouldBeThrown) {
// Great, that means the code is working as designed, so, just...
return;
}
// If we get here, an exception was thrown, but it wasn't the right type.
// Let's just throw it, the test framework will register it as a fail.
throw e;
}
// If we get here, the exception was NOT thrown, and that's bad, so..
Assert.fail("Expected exception " + expected + " but didn't see it.");
}
Now that you know how it works, you can write it yourself and thus add or change or log or whatever you want to do during this process at the right place. However given you know it's IOException, instead of an instanceof check you can just catch (IOException e), simpler.
I have used the following code to write elements from an arraylist into a file, to be retrieved later on using StringTokenizer. It works perfect for 3 other arraylists but somehow for this particular one, it throws an exception when reading with .nextToken() and further troubleshooting with .countTokens() shows that it only has 1 token in the file. The delimiters for both write and read are the same - "," as per the other arraylists as well.
I'm puzzled why it doesnt work the way it should as with the other arrays when I have not changed the code structure.
=================Writing to file==================
public static void copy_TimeZonestoFile(ArrayList<AL_TimeZone> timezones, Context context){
try {
FileOutputStream fileOutputStream = context.openFileOutput("TimeZones.dat",Context.MODE_PRIVATE);
OutputStreamWriter writerFile = new OutputStreamWriter(fileOutputStream);
int TZsize = timezones.size();
for (int i = 0; i < TZsize; i++) {
writerFile.write(
timezones.get(i).getRegion() + "," +
timezones.get(i).getOffset() + "\n"
);
}
writerFile.flush();
writerFile.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
==========Reading from file (nested in thread/runnable combo)===========
public void run() {
if (fileTimeZones.exists()){
System.out.println("Timezone file exists. Loading.. File size is : " + fileTimeZones.length());
try{
savedTimeZoneList.clear();
BufferedReader reader = new BufferedReader(new InputStreamReader(openFileInput("TimeZones.dat")));
String lineFromTZfile = reader.readLine();
while (lineFromTZfile != null ){
StringTokenizer token = new StringTokenizer(lineFromTZfile,",");
AL_TimeZone timeZone = new AL_TimeZone(token.nextToken(),
token.nextToken());
savedTimeZoneList.add(timeZone);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
}
}
===================Trace======================
I/System.out: Timezone file exists. Loading.. File size is : 12373
W/System.err: java.util.NoSuchElementException
at java.util.StringTokenizer.nextToken(StringTokenizer.java:349)
at com.cryptotrac.trackerService$1R_loadTimeZones.run(trackerService.java:215)
W/System.err: at java.lang.Thread.run(Thread.java:764)
It appears that this line of your code is causing the java.util.NoSuchElementException to be thrown.
AL_TimeZone timeZone = new AL_TimeZone(token.nextToken(), token.nextToken());
That probably means that at least one of the lines in file TimeZones.dat does not contain precisely two strings separated by a single comma.
This can be easily checked by making sure that the line that you read from the file is a valid line before you try to parse it.
Using method split, of class java.lang.String, is preferable to using StringTokenizer. Indeed the javadoc of class StringTokenizer states the following.
StringTokenizer is a legacy class that is retained for compatibility reasons although its use is discouraged in new code. It is recommended that anyone seeking this functionality use the split method of String or the java.util.regex package instead.
Try the following.
String lineFromTZfile = reader.readLine();
while (lineFromTZfile != null ){
String[] tokens = lineFromTZfile.split(",");
if (tokens.length == 2) {
// valid line, proceed to handle it
}
else {
// optionally handle an invalid line - maybe write it to the app log
}
lineFromTZfile = reader.readLine(); // Read next line in file.
}
There are probably multiple things wrong, because I'd actually expect you to run into an infinite loop, because you are only reading the first line of the file and then repeatedly parse it.
You should check following things:
Make sure that you are writing the file correctly. What does the written file exactly contain? Are there new lines at the end of each line?
Make sure that the data written (in this case, "region" and "offset") never contain a comma, otherwise parsing will break. I expect that there is a very good chance that "region" contains a comma.
When reading files you always need to assume that the file (format) is broken. For example, assume that readLine will return an empty line or something that contains more or less than one comma.
on netbeans, im trying to read a file and display its contents on a swing graphics tab. This is how im reading the file
FileReader reader;
ArrayList<String> file = new ArrayList<String>();
Scanner scan = null;
try
{
reader = new FileReader(filename);
scan = new Scanner(reader);
while(scan.hasNext())
{
file.add(scan.nextLine());
}
return file;
}
catch (IOException e)
{
e.printStackTrace();
}
finally {
scan.close();
}
return null;
This is how I'm writing the file
public String writeFile(ArrayList<String> data)
{
String writer = "";
for (String line : data)
{
writer += (line + lineSeparator);
}
return writer;
}
This is how I'm trying to display it
FileIO file = new FileIO();
String filePath="squeeze.txt";
ArrayList<String> data = file.readFile(filePath);
jTextField1.setText(file.writeFile(data));
And I getting an error on
scan.close();
Your problem here is that scan has not been initialized prior to the try block. Anything in the try block might throw an exception, so therefore you have to write your code assuming that all code in the try block will never be run. Luckily, Java has a syntax just for this situation called try-with-resources. Try-with-resources handles your resources for you, and automatically closes them at the end of the try block. Here is your code, modified to use try-with-resources:
try (FileReader reader = new FileReader(filename);
Scanner scan = new Scanner(reader)) {
while(scan.hasNext()) {
file.add(scan.nextLine());
}
return file;
} catch (IOException e) {
e.printStackTrace();
}
return null;
I also notice that in your catch block, you simply print the stack trace. This is perfectly fine as far as syntax goes, and the compiler will accept it, but I wouldn't recommend swallowing errors like this. If you don't want to do anything special, the best all-purpose line you can use is throw new RuntimeException();. This just throws a generic runtime exception, which will print the stack trace and then terminate the program. This also has the added benefit that you don't need the return null; line at the bottom, since the runtime exception will exit the program anyway, and then any method that calls this method can safely assume that this method returns a non-null value.
jTextField1.setText(file.writeFile(data));
A JTextField is for single lines of text. For multiple lines, use a JTextArea.
As to the problem at hand, the easiest solution is to use methods available to any JTextComponent (which includes both of the above).
Namely JTextComponent.read(Reader,Object) & JTextComponent.write(Writer).
I'm currently working on a simple method which converts the content of a file to a string. I saw several topics covering some detail about this question (here). However I can use the try catch or a return do_nothing as mentioned in the previous linked answer. The code:
public static String getStringFromFile(String path) throws EmptyFileException {
String data =null;
try {
BufferedReader reader = new BufferedReader(new FileReader(path));
String line;
while ((line = reader.readLine()) != null) {
data += line +"\n";
}
if (data == null) {
throw new EmptyFileException();
}
}
catch (FileNotFoundException ex) {
showExceptionMessage("File not found");
}
catch (IOException ex) {
showExceptionMessage("Can't read the file");
}
return(data);
}
private static void showExceptionMessage(String message) {
JOptionPane.showMessageDialog(null, message, "ERROR", JOptionPane.ERROR_MESSAGE);
}
So what would be "better" throwing an exception if the file is empty or just using return doNothing() (were doNothing is the function that does nothing, makes sense right? hahah).
Is this a good way to avoid null return?
Yes, always return "existing" object instead of null if you can. Null Object Pattern is a good aproach to avoid that.
So what would be "better" throwing an exception if the file is empty
or just using return doNothing()
In your case throwing an exception is overkill for me. You should throw an exception when you don't expect particular behaviour. Have a look at FileNotFoundException. You always expect that file exists so it's a good choice to throw exception when you can't find this file.
So as metioned above null is bad, so why can't you return empty string? :) In this case it will work like Null Object Pattern, like placeholder :)
I am wondering if the below code closes InputStream in finally block correctly
InputStream is = new FileInputStream("test");
try {
for(;;) {
int b = is.read();
...
}
} finally {
try {
is.close();
} catch(IOException e) {
}
}
If an exception happens during is.read() will be it ignored / suppressed if an exception happens during is.close()?
Best way is to use Java 7 and use try with resources, or do same thing manualy and add exception from closing as suppressed exception.
Pre Java 7:
If you are throwing your custom exception, you can add in it supressed exception like it is done in Java 7 (in your exception create fields List suppressed and put there exceptions from close operation and when dealing with your exception, look there too.
If you cannot do that, I don't know anything better than just log it.
examples:
from Java tutorials
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
but better form is:
static String readFirstLineFromFile(String path) throws IOException {
try (FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr)) {
return br.readLine();
}
}
This way even if creation of FileReader is succesfull but creation of BufferedReader fails (eg not enough memory), FileReader will be closed.
You can close it with IOUtils from https://commons.apache.org/proper/commons-io/
public void readStream(InputStream ins) {
try {
//do some operation with stream
} catch (Exception ex) {
ex.printStackTrace();
} finally {
IOUtils.closeQuietly(ins);
}
}
The Java 6 specs say
If execution of the try block completes abruptly for any other reason R, then the finally block is executed. Then there is a choice:
If the finally block completes normally, then the try statement completes abruptly for reason R.
If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).
So you are right, you will lose the original exception.
The solution probably is to write your finally block so defensively that it is a bigger surprise (worth propagating) if the finally block fails than if an exception comes out of the try catch block.
So, for example, if it is possible that the stream may be null when you try to close it, check it:
InputStream is = new FileInputStream("test");
try {
for(;;) {
int b = is.read();
...
}
} finally {
try {
if( is!=null ) {
is.close();
}
} catch(IOException e) {
}
}
In Java 7, Alpedar's solution is the way to go of course.
The exception from is.close() will be suppressed and the exception from is.read() will be the one that propagates up.
With the code you posted:
If is.close() throws an IOException, it gets discarded and the original exception propagates.
If is.close() throws something else (a RuntimeException or an Error), it propagates and the original exception is discarded.
With Java 7, the correct way to close an InputStream without loosing the original exception is to use a try-with-resources statement:
try (InputStream is = new FileInputStream("test")) {
for(;;) {
int b = is.read();
// ...
}
}
Prior to Java 7, what you do is just fine, except you may want to catch all exceptions instead of just IOExceptions.
Based on your code sample if an exception occurs at the int b = is.read(); point, then the exception will be raised higher up the call chain.
Note though that the finally block will still execute and if the Inputstream invalid another exception will be thrown, but this exception will be "swallowed", which may be acceptable depending on your use case.
Edit:
Based on the title of your question, I would add that what you have is fine in my opinion. You may want to additionally add a catch block to explicitly handle (or perhaps wrap) any exception within the first try block, but it is also acceptable to let any IO exceptions raise up - this really depends on your API. It may or may not be acceptable to let IO exceptions raise up. If it is, then what you have it fine - if it isn't then you may want to handle/wrap the IO exception with something more suitable to your program.
How about the next solution:
InputStream is = new FileInputStream("test");
Exception foundException=null;
try {
for(;;) {
int b = is.read();
...
}
} catch (Exception e){
foundException=e;
}
finally {
if(is!=null)
try {
is.close();
} catch(IOException e) {
}
}
//handle foundException here if needed
If an exception happens during is.read() will be it ignored / suppressed if an exception happens during is.close()?
Yes. You have a catch block for the exception in close() which does not re-throw the exception. Ergo it is not propagated or rethrown.
This is the sample to help to understand your problem,
if you declare the scanner in the try-catch block it will give compiler warning the resource is not closed.
so either make it locally or just in try()
import java.util.InputMismatchException;
import java.util.Scanner;
class ScanInt {
public static void main(String[] args) {
System.out.println("Type an integer in the console: ");
try (Scanner consoleScanner = new Scanner(System.in);) {
System.out.println("You typed the integer value: "
+ consoleScanner.nextInt());
} catch (InputMismatchException | ArrayIndexOutOfBoundsException exception) {
System.out.println("Catch Bowled");
exception.printStackTrace();
}
System.out.println("----------------");
}
}