I'm having trouble finding a clean way to handle checked exceptions thrown by a lambda function passed as a parameter in Java.
I'm writing a Client which will interact via RMI with a Server to play a game; a Client needs to be registered on the Server to be able to login, and it needs to be logged in to play a game. It's a college project, so I have some constraint on what technologies I can use and what I need to do. In particular, when a method raises a RemoteException
I've been asked to retry to call the same method a few times. I'm using Java 8 and I wanted to experiment a little bit with lambdas.
The class I'm focusing on right now will be the interface the client will use to communicate with the server; as of now I have the following structure.
Note: most of the methods I need to implement are still to be written; I'm using a custom exception to remember to implement them.
ServerRMI.java
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface ServerRMI extends Remote {
public static final String name = "hangman_server_rmi_name";
public int login(String nick, String password)
throws WrongPasswordException,
RemoteException;
public void logout(String nick, int cookie)
throws UserNotLoggedException,
RemoteException;
public void register(String nick, String password)
throws UserAlreadyRegisteredException,
RemoteException;
}
ServerInterface.java
import java.net.InetAddress;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class ServerInterface {
private String nickname;
private InetAddress address;
private int port;
private ServerRMI serverRMI;
public ServerInterface(String nickname, InetAddress address, int port)
throws RemoteException, NotBoundException{
this.nickname = nickname;
this.address = address;
this.port = port;
this.serverRMI = getServerRMI(address, port);
}
private interface ConnectionAction<R,E extends Throwable> {
public R action() throws E;
}
private static <E extends Throwable,R> R tryManyTimes(int times, ConnectionAction<R,E> action)
throws E {
if (times <= 0) {
throw new IllegalArgumentException("Expected times > 0");
}
E lastException = null;
boolean done = false;
while (times > 0 && !done) {
try {
return action.action();
} catch (Exception e) {
times--;
lastException = (E) e;
}
}
throw lastException;
}
private static ServerRMI getServerRMI(InetAddress parAddress, int parPort)
throws RemoteException, NotBoundException {
final InetAddress address = parAddress;
final int port = parPort;
try {
final Registry registry = (Registry) tryManyTimes(3,
() -> //\
LocateRegistry.getRegistry(address.getHostAddress(), port));
return (ServerRMI) tryManyTimes(3, () -> registry.lookup(ServerRMI.name));
} catch (Exception e) {
if (e instanceof RemoteException) {
throw (RemoteException) e;
} else if (e instanceof NotBoundException) {
throw (NotBoundException) e;
} else {
throw new RuntimeException("Unexpected exception");
}
}
}
public void register(String password)
throws UserAlreadyRegisteredException,
ConnectionFailureException {
throw new NotImplementedException();
}
public void login(String password)
throws WrongPasswordException,
ConnectionFailureException {
throw new NotImplementedException();
}
public void logout()
throws IllegalStateException,
ConnectionFailureException {
throw new NotImplementedException();
}
public Game openGame(int players)
throws IllegalStateException,
GameAlreadyExistsException,
ConnectionFailureException {
throw new NotImplementedException();
}
public Game joinGame(String name)
throws IllegalStateException,
GameNotFoundException,
ConnectionFailureException {
throw new NotImplementedException();
}
Game.java
public class Game {
}
I'm not including the files with the custom exceptions I've defined; every custom exception, right now, extends RuntimeException
.
Is there some way to organize the code that lets me avoid the instanceof
uses in the body of getServerRMI
? Ideally, I would like to do something like:
catch (RemoteException | NotBoundException e) {
throw e;
}
Any suggestion on any other part of the code will be appreciated.
1 Answer 1
Firstly, a couple of comments:
ServerInterface
is a bad name for something that isn't an interface
.
Why do you have //\
before your lambda's body? Java is perfectly content with having it on a new line. As far as I can tell, this is useless.
Now, with regards to your actual question: Yes, you can do exactly that. It's literally that syntax. You can also put multiple catch
blocks, so instead of that instanceof
mess, you can have this:
catch (RemoteException | NotBoundException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Unexpected exception");
}
It checks each in order, and subclasses are caught with any statement that matches their superclass.
Now, there are a couple of issues with your approach:
- You're throwing a plain
RuntimeException
. This is typically a bad idea, because if you throw a more specific one, people can catch situations where their own exceptional case occurs. - You don't give any information about what caused the exception, neither in the message nor through the exception itself.
You can fix both problems by either:
- Creating your own custom exception class that carries the information, or
Use the
RuntimeException(String, Throwable)
constructor, and use a more informative message. That call would look something like:throw new RuntimeException("Unexpected exception: " + e.getMessage(), e);
Aside from that, I can't see that much wrong with your code. Once I get a chance I'll run it through Eclipse and see if it turns up anything -- if I haven't in 24 hours or so, please reply here and remind me.
-
\$\begingroup\$ I've been having a hard time coming up with a different name for
ServerInterface
; it is, after all, what I use to interface with the server. Maybe something on the line ofServerCommunicator
might be an alternative. Nice suggestion about theRuntimeException
, I didn't think of fixing it this way! Thanks for the tips \$\endgroup\$gcali– gcali2015年07月01日 07:53:07 +00:00Commented Jul 1, 2015 at 7:53 -
\$\begingroup\$ @Odexios Hmm, good point -- there is the alternate definition that has nothing to do with programming. It's not a bad name, it just might be confusion, since "interface" has a specific meaning in Java.
ServerConnection
orServerCommunicator
could be a better name, but in the end, it's not that bad, and could probably be left alone. \$\endgroup\$anon– anon2015年07月01日 07:56:49 +00:00Commented Jul 1, 2015 at 7:56
} catch (Exception e) {....}
as well. \$\endgroup\$catch (Exception e)
block; it works perfectly. If anyone wants to write an answer I'll accept it, otherwise I'll write one later. \$\endgroup\$