Плохая идея связать исключения цепочкой с RMI?
-
20-08-2019 - |
Вопрос
Плохая ли идея использовать цепочку исключений при выбрасывании RemoteExceptions?У нас есть сервер RMI, который делает что-то вроде этого:
public Object doSomething() throws RemoteException
{
try
{
return getData();
}
catch (CustomException ex)
{
throw new RemoteException(ex);
}
}
Я получаю исключение UnmarshallException, вызванное ClassNotFoundException в моем клиенте.С положительной стороны, оказывается, что экспортируется само CustomException.К сожалению, другое исключение глубоко внутри этого парня НЕ экспортируется, и именно здесь возникает ClassNotFoundException .Я думаю, что иерархия выглядит примерно так:
RemoteException -> CustomException -> SQLException -> NotExportedException
Проблема, которую я вижу, заключается в том, что, хотя мы можем гарантировать, что CustomException экспортируется, мы не можем гарантировать, что экспортируются какие-либо исключения более низкого уровня.
Из-за этого я склоняюсь к ТОМУ, чтобы НИКОГДА НЕ использовать цепочку исключений с RemoteExceptions.Вместо этого, я думаю, мне, вероятно, следует зарегистрировать трассировку стека на стороне сервера и выдать простое ванильное исключение RemoteException без привязки к нему исключения "причина".Кто-нибудь сталкивался с такой ситуацией раньше?
Решение
Вместо того чтобы оборачивать CustomException в RemoteException, вы могли бы изменить свой удаленный интерфейс следующим образом:
interface Foo extends Remote {
Object doSomething() throws CustomException, RemoteException;
}
Принцип здесь заключается в том, что только среда выполнения RMI должна вызывать RemoteExceptions;они сигнализируют о каком-то сбое в удаленном взаимодействии, а не в логике приложения.Фактически, конкретным реализациям даже не нужно объявлять RemoteException
.
Но это не относится к случаю, когда ваш сервис перехватывает исключение из какой-либо сторонней библиотеки, но не хочет раскрывать это в предложении throws .
public Object doSomething() throws CustomException {
try {
return theirSvc.getData();
} catch (ThirdPartyException ex) {
throw new CustomException("Failed to obtain requested data.");
// or: throw new CustomException("Failed to obtain requested data.", ex) ?
}
}
В этом случае я рекомендую вам не создавать "дырявую абстракцию", где зависимость создавалась бы в клиенте, которому в противном случае не было бы необходимости знать об этой сторонней библиотеке.
Обычно ведение журнала и выбрасывание - плохая практика, потому что одна и та же ошибка регистрируется повторно.Но в данном случае, я думаю, это оправдано, поскольку сгенерированное исключение передается клиенту;может быть полезно зарегистрировать его как на клиенте, так и на сервере.Таким образом, блок catch в конечном итоге выглядит следующим образом:
catch (ThirdPartyException ex) {
String message = "Failed to obtain requested data.";
log.error(message, ex);
throw new CustomException(message);
}
Таким образом, зависимость ThirdPartyException ограничена сервером, журналы сервера содержат соответствующую информацию, относящуюся к конкретной реализации, и клиенту правильно сообщается об ошибке.
Другие советы
Мы фиксируем сообщение + трассировку всего стека из исходного исключения и передаем это как содержимое удаленного исключения.Таким образом, вы получаете все сведения о стеке, но вам не нужно беспокоиться о том, что какое-либо из внутренних исключений не является сериализуемым.
Вы никогда не знаете, какие другие объекты могут находиться внутри какой-либо другой третьей стороны (или даже ваших собственных пользовательских исключений "первой стороны"!).