122 lines
6.1 KiB
TeX
122 lines
6.1 KiB
TeX
\part{Exception Handling}
|
|
\section{Checked und Runtime Exceptions in Java}
|
|
\subsection{Checked Exceptions (z.B. SQL-Exception)}
|
|
\begin{itemize}
|
|
\item leiten von Exception Klasse ab und müssen behandelt werden (trows - catch)
|
|
\item Verwendung für Probleme die durch User behoben werden können (alternative Aktion)
|
|
\end{itemize}
|
|
\subsection{Unchecked Exceptions (z.B. NullPointerException)}
|
|
\begin{itemize}
|
|
\item leiten von RuntimeException ab
|
|
\item Verwendung für technische Probleme (User kann nichts machen außer neu starten)
|
|
\begin{itemize}
|
|
\item beschädigte Datenbank - die Exception geht durch alle Layer
|
|
\begin{itemize}
|
|
\item erst mit Implementierungsspezifischer Exception
|
|
\item später mit Runtime \textbf{ohne Stacktrace} bis zum User (-> Destructive wrapping mit Log and Throw im ServiceLayer)
|
|
\item im Projekt: ServiceException, DaoException, AuthenticationException und SerializerException
|
|
\end{itemize}
|
|
\end{itemize}
|
|
\end{itemize}
|
|
\begin{minted}[linenos,breaklines=true]{java}
|
|
package at.fhj.swd.psoe.service;
|
|
|
|
public class ServiceException extends RuntimeException {
|
|
private static final long serialVersionUID = -1109707847007116930L;
|
|
|
|
public ServiceException(String message) {super(message);}}
|
|
\end{minted}
|
|
\begin{minted}[linenos,breaklines=true]{java}
|
|
package at.fhj.swd.psoe.data;
|
|
|
|
public class DaoException extends RuntimeException {
|
|
private static final long serialVersionUID = -2712863481296295032L;
|
|
|
|
public DaoException(String message, Throwable cause) {
|
|
super(message, cause);
|
|
}
|
|
public DaoException(Throwable cause) {super(cause);}}
|
|
\end{minted}
|
|
\begin{figure}[!htp]
|
|
\centering
|
|
\includegraphics[width=0.3\textwidth]{pics/except_class_dia.jpg}
|
|
\end{figure}
|
|
\section{Best Practice Beispiele beim Einsatz von Exceptions}
|
|
\begin{itemize}
|
|
\item Exceptions nicht für Programmflusskontrolle verwenden (schlechte Performance)
|
|
\item offene Ressourcen schließen (try-with-resources bzw. close im finally)
|
|
\item selbst erstellte Exceptions auch mit nützlichen Infos ausstatten
|
|
\item Implementierungsspezifische Exceptions nicht bis zum User durchwerfen (stattdessen catch + trow RuntimeException)
|
|
\item dokumentieren mit @trows im DOC, testen mit JUnit
|
|
\end{itemize}
|
|
\section{Exception Handling Anti Pattern}
|
|
\begin{itemize}
|
|
\item Log and Trow (nie beides: entweder, oder)
|
|
\item Trowing Exception bzw. catch Exception (spezifischere anstatt Basisklasse verwenden)
|
|
\item Destructive Wrapping (wenn bei catch + trow = wrapping nicht die Original Exception weitergegeben wird)
|
|
\item Log and return Null (provoziert an einer anderen Stelle eine NullPointerException)
|
|
\item Catch and Ignore
|
|
\item Unsupported Operation return Null (besser UnsupportedOperationException)
|
|
\end{itemize}
|
|
\section{Destructive Wrapping im Service Layer}
|
|
\begin{itemize}
|
|
\item im Codebeispiel wird in Zeile 12 eine IllegalArgumentException (Runtime) gefangen in Zeile 18 die Exception allgemein
|
|
\item beide werden in Zeile 14 bzw. 18 inklusive Stacktrace geloggt
|
|
\item ausnahmsweise muss hier zusätzlich auch eine neue ServiceException geschmissen werden, jedoch \textbf{ohne Stacktrace} (siehe Zeile 15 und 19)
|
|
\end{itemize}
|
|
\begin{minted}[linenos,breaklines=true]{java}
|
|
package at.fhj.swd.psoe.service.impl;
|
|
...
|
|
@Override
|
|
public List<DocumentDTO> getDocumentsFromCommunity(Long communityID) {
|
|
try {
|
|
List<Document> documents;
|
|
if (communityID <= 0) throw new IllegalArgumentException("community must not be empty");
|
|
Community community = communityDAO.findById(communityID);
|
|
if (community == null) throw new IllegalStateException("community " + communityID + " not found");
|
|
documents = documentDAO.findByCommunity(community);
|
|
return documents.stream().map(DocumentMapper::toDTO).collect(Collectors.toList());
|
|
} catch (IllegalArgumentException iaex) {
|
|
String errorMsg = "Could not load docs from community (illegal argument)";
|
|
logger.error(errorMsg, iaex);
|
|
throw new ServiceException(errorMsg);
|
|
} catch (Exception ex) {
|
|
String errorMsg = "Could not load docs for community.";
|
|
logger.error(errorMsg + " id " + communityID, ex);
|
|
throw new ServiceException(errorMsg);
|
|
}
|
|
}
|
|
...
|
|
\end{minted}
|
|
\section{Exception Testing}
|
|
\begin{minted}[linenos,breaklines=true]{java}
|
|
package at.fhj.swd.psoe.service;
|
|
|
|
import java.util.ArrayList;
|
|
...
|
|
|
|
@RunWith(MockitoJUnitRunner.Silent.class)
|
|
public class DocumentServiceTest {
|
|
@Mock
|
|
private DocumentDAO documentDAO;
|
|
...
|
|
|
|
@Test(expected = ServiceException.class)
|
|
public void getDocumentsFromCommunity_WithId0_ShouldFail() {
|
|
documentService.getDocumentsFromCommunity(0L);
|
|
}
|
|
|
|
@Test(expected = ServiceException.class)
|
|
public void getDocumentsFromCommunity_NoDocuments_ShouldFail() {
|
|
Community community = Mockito.mock(Community.class);
|
|
Mockito.when(community.getId()).thenReturn(COMMUNITYID);
|
|
Documentlibrary doclib = Mockito.mock(Documentlibrary.class);
|
|
Mockito.when(community.getDocumentlibrary()).thenReturn(doclib);
|
|
Mockito.when(doclib.getCommunity()).thenReturn(community);
|
|
Mockito.when(communityDAO.findById(community.getId())).thenReturn(null);
|
|
|
|
documentService.getDocumentsFromCommunity(COMMUNITYID);
|
|
}
|
|
...
|
|
\end{minted}
|