-
help with exceptions
Hello,
I have a rather difficult class assignment and I need some help with it.
We are suppossed to write an application that simulates storing data in a db server.
Three interfaces and one class were provided:
Code:
public interface Connection {
/**
* Enables to send a data to the remote host.
*
* @param data data to be sent
* @throws IOException indicates that an error occured while sending data
* to the remote host (usually due to a temporary network failure).
*/
void sendData(Object data) throws IOException;
}
Code:
public interface Storage {
/**
* Stores data in remote Db server.
*
* @param host Address of remote DB server
* @param obj Data to store
* @throws DbException if the storage fails for some reason
*/
void store(String host, Object data) throws DbException;
}
Code:
public interface Connector {
/**
* Establishes connection to remote host.
*
* @param host remote host address
* @return Established connection with the remote host
* @throws UnknownHostException Indicates that the host address does not exist
* @throws NoRouteToHostException Signals that an error occurred while
* attempting to connect a socket to a remote address.
*/
Connection getConnection(String host)
throws UnknownHostException, NoRouteToHostException;
}
Code:
public class UnrealibleConnector implements Connector {
Random random = new Random();
int retries = random.nextInt(10);
boolean nrthFailure = false;
@Override
public Connection getConnection(String target)
throws UnknownHostException, NoRouteToHostException {
retries--;
if (retries <= 0) {
retries = random.nextInt(10);
if (nrthFailure = !nrthFailure) {
throw new NoRouteToHostException("No route to " + target
+ " (counter: " + retries + ").");
} else {
throw new UnknownHostException("No host: " + target
+ " (counter: " + retries + ").");
}
} else {
return new SimpleConnection(target);
}
}
private class SimpleConnection implements Connection {
private int retries = random.nextInt(8);
private String host;
private SimpleConnection(String host) {
this.host = host;
}
@Override
public void sendData(Object data) throws IOException {
retries--;
if (retries > 0) {
throw new IOException("Cannot send data (counter: " + retries + ")");
} else {
retries = random.nextInt(8);
System.out.println("Data '" + data + "' sent to '" + host + "'.");
}
}
}
}
I created three custom exceptions.
Code:
public class DbException extends Exception {
public DbException(String message, Throwable cause) {
message = "Error during communication with the remote server.";
}
}
Code:
public class CannotStoreException extends DbException
{
public CannotStoreException(String message, Throwable cause) {
super(message, cause);
}
}
Code:
public class DbUnreachableException extends DbException
{
public DbUnreachableException(String message, Throwable cause) {
super(message, cause);
}
}
Now the task is to create a class that
- implements Storage interface
- should have a constructor with two parameters - Connector connector, int maxAttempts
- establish (simulated) connection using an instance of the Connector class a then send data
- if the connection cant be established (Connector throws exception), data storing is immediatelly interpupted with a DbUnreachableException
- if there is an error during data sending (Connection throws exception), method should try to send data again; max number of tries is maxAttempts
- if data cant be stored even after this number of attempts, CannotStoreException should be thrown
Here is what I have so far:
Code:
public class MyStorage implements Storage
{
private Connector connector;
private int maxAttempts;
public MyStorage(Connector connector, int maxAttempts) throws NullPointerException, IllegalArgumentException {
if(connector == null) throw new NullPointerException("connector");
if(maxAttempts < 1) throw new IllegalArgumentException("maxAttempts");
this.connector = connector;
this.maxAttempts = maxAttempts;
}
public void store(String host, Object data) throws DbException {
try {
Connector c = new UnrealibleConnector();
c.getConnection(host);
} catch (Exception e) {
e.getClass();
e.printStackTrace();
}
}
Now I donīt know how to send data - sendData(data) does not work for me.
Also, why am I not able to catch my custom exception in the catch block? The compiler wont allow me.
Thank you for any tips.
-
Re: help with exceptions
"does not work for me"...what does not work. How is it not working?
Same with "compiler won't allow me".
You need to give us the code where you tried these things and the full errors you are getting.
-
Re: help with exceptions
Ok, sorry about that.
The above code compiles without a problem, but the store(String host, Object data) method does not work, as it does not establish the connection.
When I try to use my custom exception DbUnreachableException, the compiler says "DbUnreachableException is never thrown in body of corresponding try statement".
-
Re: help with exceptions
What happens when you run it then?
And yes, none of the methods in that try block throw your exception, so no surprise you can't catch it.
These are your tasks:
1- implements Storage interface
2- should have a constructor with two parameters - Connector connector, int maxAttempts
3- establish (simulated) connection using an instance of the Connector class a then send data
4- if the connection cant be established (Connector throws exception), data storing is immediatelly interpupted with a DbUnreachableException
5- if there is an error during data sending (Connection throws exception), method should try to send data again; max number of tries is maxAttempts
6- if data cant be stored even after this number of attempts, CannotStoreException should be thrown
You've done 1 and 2 and part of 3.
You are not handling the Connectors exception and throwing a DbUnreachableException, so 4 is untouched.
Same with 5.
Same with 6.
To do 4 you need to catch the relevant exception (not just all exceptions) and throw your exception.
-
Re: help with exceptions
I tried this:
Code:
public void store(String host, Object data) throws DbException {
try {
Connector c = new UnrealibleConnector();
c.getConnection(host);
} catch (NoRouteToHostException e) {
throw new DbException("DBE", e);
} catch (UnknownHostException e) {
throw new DbException("DBE", e);
}
}
Here is the code of the test used to check my work:
Code:
// test send data
TestConnector connector = new TestConnector();
Storage st = new MyStorage(connector,1);
Circle circle = new Circle(); // just some Object
try {
st.store("address1", circle);
} catch (Exception ex) {
fail("Method Storage.store() threw unexpected exceptions " + ex);
}
assertEquals("Connection was not established with correct address","" +
"address1",connector.host);
assertEquals("During sending an object to remote server the object was not passed to connector",
circle,connector.sentData);
assertEquals("Method Connector.getConnection() was called more times than it had to be",
1,connector.getConnectionCounter);
assertEquals("Method Connection.sendData() was called more times than it had to be",
1,connector.sendDataCounter);
Again, it compiles, but the test says the same - the problem is on this line: assertEquals("Connection was not established with correct address","" + "address1",connector.host);
-
Re: help with exceptions
Well, for starters, the connector you are using in the assertEquals is not the connector you are using in the store() method, so not surprising it doesn't match.
The store() method should be using the connector that is your attribute, not create a new one.
-
Re: help with exceptions
Corrected:
Code:
public void store(String host, Object data) throws DbException {
try {
connector.getConnection(host);
} catch (NoRouteToHostException e) {
throw new DbException("DBE", e);
} catch (UnknownHostException e) {
throw new DbException("DBE", e);
}
}
I still ainīt sure how to send the data, because method sendData(Object data) throws IOException; is in the interface and it cant be instantiated.
The test fails at the next line - "assertEquals("During sending an object to remote server the object was not passed to connector",circle,connector.sentData);"
-
Re: help with exceptions
getConnection returns the Connection object, so you need to assign that:
Code:
Connection conn = connector.getConnection(host);
and then you can call the method on that object.
-
Re: help with exceptions
Oh yes, make sure you are throwing the correct exception...the one the requirements say you should throw for each problem you can encounter (points 4 and 6).
-
Re: help with exceptions
Ok. Now the connection is established. Thanks.
Code:
public void store(String host, Object data) throws DbException {
try {
Connection conn = connector.getConnection(host);
conn.sendData(data);
} catch (IOException e) {
throw new DbUnreachableException("IOE", e);
}
}
Code:
// test connection with unknown host
connector = new TestConnector();
st = new MyStorage(connector,5);
connector.connectionException = new UnknownHostException();
try {
st.store("address1", circle);
fail("Metoda MyStorage.store() nevyhodi vyjimku, " +
"ackoliv se nepodarilo navazat spojeni a metoda " +
"Connector.getConnection() vyhodila vyjimku UnknownHostException");
} catch (DbUnreachableException ex) {
assertSame("Exception DbUnreachableException thrown as a reaction on " +
"the exception UnknownHostException does not have the " +
"right cause set",connector.connectionException,ex.getCause());
assertNotNull("Vyjimka DbUnreachableException vyvolana jako reakce na " +
"vyjimku UnknownHostException nema nastavenou smysluplnou zpravu " +
"o chybe",ex.getMessage());
} catch (Exception ex) {
fail("Metoda MyStorage.store() misto ocekavane vyjimky " +
"DbUnreachableException vyhodila vyjimku " + ex);
}
assertEquals("Metoda Connector.getConnection() byla zbytecne volana vicekrat",
1,connector.getConnectionCounter);
Donīt worry about the language, I translated the relevant text.
It seems my DbUnreachableException is not correct, as the test stops at "assertSame("Exception DbUnreachableException thrown as a reaction on the exception UnknownHostException does not have the right cause set ", connector.connectionException,ex.getCause());"
So I probably have to set the cause for my custom exceptions, right?
-
Re: help with exceptions
Your DbException class should pass the message and cause up to the Exception constructor, same as the subclasses.
Remove the 'message' assignment and replace it with a 'super(blah blah)' call.
-
Re: help with exceptions
Great, thanks. I did not realize the Exception hierarchy.
Now my DbException looks like this:
Code:
public class DbException extends Exception {
public DbException(String message, Throwable cause) {
super("bla bla", cause);
}
}
A MyStorage class:
Code:
public void store(String host, Object data) throws DbException {
try {
Connection conn = connector.getConnection(host);
conn.sendData(data);
} catch (UnknownHostException e) {
throw new DbUnreachableException("", e);
}
catch (NoRouteToHostException e) {
throw new DbUnreachableException("", e);
}
catch (IOException e) {
throw new CannotStoreException("", e);
}
}
Now I have to retry the connection. If I try to add this piece of code:
Code:
try {
Connection conn = connector.getConnection(host);
for(int i = 1; i < maxAttempts; i++) conn.sendData(data);
} catch (IOException e) {
throw new CannotStoreException("", e);
}
It will even execute even if the Connection will be successful.
The test says "assertEquals("Method Connector.getConnection() was called more times than it had to be",1,connector.getConnectionCounter);"
-
Re: help with exceptions
Pass the 'message' through as well, not just my "blah blah" placeholder.
:)
-
Re: help with exceptions
Oh yes, as for the mutliple connections, reread requirement 5:
5- if there is an error during data sending (Connection throws exception), method should try to send data again; max number of tries is maxAttempts
So you only need to retry if there is an exception.
-
Re: help with exceptions
So I tried to put the loop: for(int i = 1; i < maxAttempts; i++) conn.sendData(data);
into the catch block of the IOException.
In this case the compiler says the loop is an unreachable statement:
Code:
catch (IOException e) {
throw new CannotStoreException("", e);
for(int i = 1; i < maxAttempts; i++) conn.sendData(data);
}
In this case the compiler says "unreported exception java.io.IOException; must be caught or declared to be thrown"
Code:
catch (IOException e) {
for(int i = 1; i < maxAttempts; i++) conn.sendData(data);
throw new CannotStoreException("", e);
}
Now Iīm really stuck.
-
Re: help with exceptions
The logic is:
Code:
WHILE retries < maxRetry and failed send
SEND data
succesful send
CATCH exception
increment retry
Slightly hard to pseudo-code try/catch blocks...:)
Essentially you need another try/catch block inside a while loop (or do-while).
That try/catch block handles the IOException, not the external one.
The catch does not throw an exception unless the maxTries has been reached.
-
Re: help with exceptions
Well, I added to my code:
Code:
private int retries = 1;
while(retries < maxAttempts) {
try {
conn.sendData(data);
retries++;
} catch (IOException e) {
throw new CannotStoreException("", e);
}
}
and it still says the method MyStorage.store() threw unexpected CannotStoreException() - it relates to the situation, where Connection.sendData() throws exception for the first time and succeds on the second time.
How can I write if/while statement, where the condition is (sendData throws Exception)?
-
Re: help with exceptions
The increment is in the catch.
You only throw the exception if you've used up all your retries.
The last part of the try block should exit the loop, since it's succeeded.
-
Re: help with exceptions
Code:
while(retries < maxAttempts) {
try {
conn.sendData(data);
break;
} catch (IOException e) {
retries++;
if(retries == maxAttempts) throw new CannotStoreException("", e);
}
}
:(think):
-
Re: help with exceptions