PSE_learning/parts/02_exceptions.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}