-
Notifications
You must be signed in to change notification settings - Fork 13
MCR-3258 Enable PMD rule ExceptionAsFlowControl #2480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 2024.06.x
Are you sure you want to change the base?
MCR-3258 Enable PMD rule ExceptionAsFlowControl #2480
Conversation
Alles "schwierig": Ein paar einfache Fälle konnte ich lösen.
Ich tendiere die einfachen Fälle zu mergen, aber dann die Regel ggf. sogar ganz zu deaktivieren. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't finish with all of them will continue later.
@@ -113,6 +113,7 @@ private void findInConfigResourcesDir(File configDir) throws IOException { | |||
configDir.toPath().relativize(file).toString())); | |||
} | |||
|
|||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can try:
Simplify findInConfigLibDir.
private void findInConfigLibDir(File configDir) throws IOException {
final Path lib = configDir.toPath().resolve("lib");
doForFilesRecursive(lib, n -> n.endsWith(".jar"),
file -> {
try (InputStream in = Files.newInputStream(file)) {
findInJarInputStream(configDir.toPath().relativize(file).toString(), in);
}
});
}
Change method signature to:
private void doForFilesRecursive(Path baseDir, Predicate<String> lowerCaseCheck,
PathConsumerWithIOException pathConsumer)
Add functional interface:
@FunctionalInterface
interface PathConsumerWithIOException {
void accept(Path path) throws IOException;
}
@@ -130,7 +130,7 @@ protected MCRStoredNode buildChildNode(Path fo) { | |||
* Repairs additional metadata of this directory and all its children | |||
*/ | |||
@Override | |||
@SuppressWarnings("unchecked") | |||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe don't use a stream, this actually looks simpler:
try (Stream<MCRNode> streamMCRNode = getChildren()) {
for (Iterator<MCRNode> iterator = streamMCRNode.iterator(); iterator.hasNext(); ) {
MCRNode node = iterator.next();
if (node instanceof MCRStoredNode storedNode) {
storedNode.repairMetadata();
}
}
}
@@ -131,6 +131,7 @@ private void readAdditionalData() throws IOException { | |||
} | |||
} | |||
|
|||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this code. It looks way to complicated. @yagee-de can you fix this?
@@ -124,6 +124,7 @@ public static List<String> writeMD5SumsToDirectory(String directory) throws NotD | |||
.collect(Collectors.toList()); | |||
} | |||
|
|||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use iterator again:
@MCRCommand(syntax = "generate md5sum file {0} for ifs2 file store {1}",
help = "writes md5sum file {0} for every file in MCRFileStore with ID {1}")
public static void writeMD5SumFile(String targetFile, String ifsStoreId) throws IOException {
initFileStores();
final MCRStore store = MCRStoreCenter.getInstance().getStore(ifsStoreId);
if (!(store instanceof MCRFileStore)) {
throw new MCRException("Store " + ifsStoreId + " is not found or is not a file store.");
}
Path targetPath = Paths.get(targetFile);
if (!Files.isDirectory(targetPath.getParent())) {
throw new NotDirectoryException(targetPath.getParent().toString());
}
try (BufferedWriter bufferedWriter = Files.newBufferedWriter(targetPath, StandardCharsets.UTF_8,
StandardOpenOption.CREATE)) {
final MessageFormat md5FileFormat = new MessageFormat("{0} {1}\n", Locale.ROOT);
MCRFileStore fileStore = (MCRFileStore) store;
Stream<MCRFileCollection> fileCollectionsStream = fileStore.getStoredIDs()
.sorted()
.mapToObj(fileStore::retrieve);
for (Iterator<MCRFileCollection> fcIterator = fileCollectionsStream.iterator(); fcIterator.hasNext();) {
MCRFileCollection fileCollection = fcIterator.next();
writeMd5EntriesForCollection(fileCollection, bufferedWriter, md5FileFormat);
}
}
}
private static void writeMd5EntriesForCollection(MCRFileCollection fileCollection, BufferedWriter bufferedWriter,
MessageFormat md5FileFormat) throws IOException {
try (Stream<FileInfo> filesStream =
getAllFiles(fileCollection.getLocalPath(), fileCollection.getMetadata().getRootElement())) {
for (Iterator<FileInfo> fileIterator = filesStream.iterator();
fileIterator.hasNext();) {
FileInfo fileInfo = fileIterator.next();
bufferedWriter.write(md5FileFormat.format(new Object[] { fileInfo.md5, fileInfo.localPath }));
}
}
}
@@ -167,6 +168,7 @@ public static void writeMD5SumFile(String targetFile, String ifsStoreId) throws | |||
} | |||
} | |||
|
|||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getAllFiles() doesnt even throw an IOException :)
private static Stream<FileInfo> getAllFiles(Path nodePath, Element node) {
Function<Element, String> getName = e -> e.getAttributeValue("name");
return node.getChildren().stream()
.sorted(Comparator.comparing(getName))
.flatMap(n -> {
final String fileName = getName.apply(n);
if ("dir".equals(n.getName())) {
return getAllFiles(nodePath.resolve(fileName), n);
}
return Stream.of(new FileInfo(nodePath.resolve(fileName), n.getAttributeValue("md5")));
});
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to move the annotation to the method instead of the whole class.
We have 3 problems here:
- getMCRFile for a MCRPath
I think we can just move the inner try out of it and it should be fixed.
static MCRFile getMCRFile(MCRPath ifsPath, boolean create, boolean createNew, boolean fireCreateEvent)
throws IOException {
if (!ifsPath.isAbsolute()) {
throw new IllegalArgumentException("'path' needs to be absolute.");
}
MCRFile file;
MCRDirectory root;
boolean rootCreated = false;
try {
root = getFileCollection(ifsPath.getOwner());
} catch (NoSuchFileException e) {
if (create || createNew) {
MCRObjectID derId = MCRObjectID.getInstance(ifsPath.getOwner());
root = getStore(derId.getBase()).create(derId.getNumberAsInteger());
rootCreated = true;
} else {
throw e;
}
}
try {
MCRPath relativePath = toPath(root).relativize(ifsPath);
file = getMCRFile(root, relativePath, create, createNew, fireCreateEvent);
} catch (Exception e) {
if (rootCreated) {
LOGGER.error("Exception while getting MCRFile {}. Removing created filesystem nodes.", ifsPath);
try {
root.delete();
} catch (Exception de) {
LOGGER.fatal("Error while deleting file system node: {}", root.getName(), de);
}
}
throw e;
}
return file;
}
- getMCRFile for MCRDirectory
Here I would just move the declaration of MCRFile file and the FileAlreadyExistException out of the try block
Deque<MCRStoredNode> created = new ArrayDeque<>();
MCRFile file = (MCRFile) baseDir.getNodeByPath(ifsPath.toString());
if (file != null && createNew) {
throw new FileAlreadyExistsException(toPath(baseDir).resolve(ifsPath).toString());
}
try {
if (file == null & (create || createNew)) {
- getObjectBaseIds
Maybe just do it in two steps. Also looks more readable:
static Collection<String> getObjectBaseIds() throws IOException {
final Path baseDir = Paths.get(getBaseDir());
if (!Files.isDirectory(baseDir)) {
return List.of();
}
// Get the list of subdirectories in baseDir.
List<Path> baseDirDirs;
try (Stream<Path> baseDirEntries = Files.list(baseDir)) {
baseDirDirs = baseDirEntries
.filter(Files::isDirectory)
.sorted(Comparator.comparing(Path::getFileName))
.toList();
}
// For each directory, list its entries and process them.
List<String> result = new ArrayList<>();
for (Path dir : baseDirDirs) {
try (Stream<Path> subDirStream = Files.list(dir)) {
subDirStream
.filter(path -> MCRObjectID.isValidType(path.getFileName().toString()))
.filter(Files::isDirectory)
.sorted(Comparator.comparing(Path::getFileName))
.map(p -> p.getParent().getFileName().toString() + "_" + p.getFileName())
.forEach(result::add);
}
}
return result;
}
@@ -126,6 +126,7 @@ public static boolean isDerivateSupported(String derivateID) { | |||
* @return if content type is in property <code>MCR.Module-iview2.SupportedContentTypes</code> | |||
* @see MCRContentTypes#probeContentType(Path) | |||
*/ | |||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would extract it into two methods:
/**
* @param file
* image file
* @return if content type is in property <code>MCR.Module-iview2.SupportedContentTypes</code>
* @see MCRContentTypes#probeContentType(Path)
*/
public static boolean isFileSupported(Path file) throws IOException {
if(file == null) {
return isContentTypeSupported(null);
}
String contentType = MCRContentTypes.probeContentType(file);
return isContentTypeSupported(contentType);
}
/**
* @param contentType
* content type
* @return if content type is in property <code>MCR.Module-iview2.SupportedContentTypes</code>
* @see MCRContentTypes#probeContentType(Path)
*/
public static boolean isContentTypeSupported(String contentType) {
if(contentType == null) {
contentType = "application/octet-stream";
}
return SUPPORTED_CONTENT_TYPE.contains(contentType);
}
@@ -62,6 +62,7 @@ public MCRTilingAction(MCRTileJob image) { | |||
* Also this updates tileJob properties of {@link MCRTileJob} in the database. | |||
*/ | |||
@Override | |||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me it looks like the inner try catch is unecessary because it's just there to add an extra log message.
I would remove the inner try catch. And also update the log message in the "real" catch message:
LOGGER.error("Error while executing tile action.", e);
@@ -496,6 +496,7 @@ public Collection<String> getObjectBaseIds() { | |||
} | |||
|
|||
@Override | |||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here the API is a bit "stupid" cause there is no "real" IOException. OCFL throws a NotFoundException which is already a runtime exception. To fix it I would suggest:
@Override
public List<MCRObjectIDDate> retrieveObjectDates(List<String> ids) throws IOException {
try {
return ids.stream()
.map(this::getOCFLObjectID)
.filter(this::isMetadata)
.filter(this::isNotDeleted)
.map(ocflId -> {
long lastModified = getLastModifiedInternal(ocflId);
return new MCRObjectIDDateImpl(new Date(lastModified), removePrefix(ocflId));
})
.collect(Collectors.toList());
} catch (Exception e) {
throw new IOException("Unable to retrieve object dates for ids " + ids, e);
}
}
@Override
public long getLastModified(MCRObjectID id) throws IOException {
return getLastModified(getOCFLObjectID(id));
}
public long getLastModified(String ocflObjectId) throws IOException {
try {
return getLastModifiedInternal(ocflObjectId);
} catch (NotFoundException e) {
throw new IOException(e);
}
}
private long getLastModifiedInternal(String ocflObjectId) {
return Date.from(getRepository()
.getObject(ObjectVersionId.head(ocflObjectId))
.getVersionInfo()
.getCreated()
.toInstant())
.getTime();
}
@@ -111,6 +111,7 @@ public String getPublicationStatus(@PathParam("objectID") String objectID) | |||
@GET | |||
@Path("publish/{objectID}") | |||
@Produces(MediaType.APPLICATION_JSON) | |||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I would catch the WebApplicationException and rethrow it. This is not an internal server error in my opinion.
} catch (WebApplicationException webApplicationException) {
throw webApplicationException;
}
@@ -488,6 +488,7 @@ public Response getThumbnail(@PathParam(PARAM_MCRID) String id, @PathParam("ext" | |||
return getThumbnail(id, defaultSize, ext); | |||
} | |||
|
|||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, method is just too big and does to much. Can you take a look @yagee-de?
@@ -270,6 +270,7 @@ private void handleCanonicalizeRequest(int compilationId, CanonicalizeRequest ca | |||
connection.sendMessage(compilationId, ProtocolUtil.inboundMessage(canonicalizeResponse.build())); | |||
} | |||
|
|||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would refactor to this:
private void handleFunctionCallRequest(int compilationId, FunctionCallRequest functionCallRequest)
throws IOException {
FunctionCallResponse.Builder response = FunctionCallResponse.newBuilder().setId(functionCallRequest.getId());
Exception exception = switch (functionCallRequest.getIdentifierCase()) {
case NAME -> new UnsupportedOperationException(
"Calling function " + functionCallRequest.getName() + " is not supported");
case FUNCTION_ID -> new UnsupportedOperationException("Calling functions by ID is not supported");
case IDENTIFIER_NOT_SET -> new IllegalArgumentException("FunctionCallRequest has no identifier");
};
LOGGER.debug("Failed to handle FunctionCallRequest", exception);
response.setError(getErrorMessage(exception));
connection.sendMessage(compilationId, ProtocolUtil.inboundMessage(response.build()));
}
@@ -108,6 +108,7 @@ public DepositReceipt getMetadata(String collectionString, MCRObject object, Opt | |||
return MCRSword.getCollection(collectionString).getMetadataProvider().provideMetadata(object); | |||
} | |||
|
|||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
streams abused...
public void deleteObject(MCRObject object) throws SwordServerException {
try {
for (MCRMetaLinkID metaLink : object.getStructure().getDerivates()) {
MCRObjectID id = metaLink.getXLinkHrefID();
MCRMetadataManager.deleteMCRDerivate(id);
}
MCRMetadataManager.delete(object);
} catch (MCRActiveLinkException | MCRAccessException | MCRPersistenceException e) {
throw new SwordServerException("Error while deleting Object.", e);
}
}
@@ -325,6 +325,7 @@ public Boolean call() throws Exception { | |||
* | |||
* @return true if command processed successfully | |||
*/ | |||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would change to:
private boolean processCommand(String command) {
if (command.trim().startsWith("#")) {
// ignore comment
return true;
}
LOGGER.info("Processing command:'{}' ({} left)", () -> command, commands::size);
setCurrentCommand(command);
long start = System.currentTimeMillis();
MCRTransactionManager.beginTransactions();
List<String> commandsReturned = null;
try {
for (List<MCRCommand> cmds : knownCommands.values()) {
commandsReturned = runCommand(command, cmds);
if (commandsReturned != null) {
break;
}
}
if (commandsReturned == null) {
LOGGER.warn("Command not understood: {}", command);
rollbackAndContinue(command);
return false;
}
updateKnownCommandsIfNeeded();
MCRTransactionManager.commitTransactions();
LOGGER.info("Command processed ({} ms)", () -> System.currentTimeMillis() - start);
} catch (Exception ex) {
LOGGER.error("Command '{}' failed. Performing transaction rollback...", command, ex);
rollbackAndContinue(command);
return false;
} finally {
// Resetting the entity manager
MCRTransactionManager.beginTransactions();
MCREntityManagerProvider.getCurrentEntityManager().clear();
MCRTransactionManager.commitTransactions();
}
return true;
}
private void rollbackAndContinue(String command) {
try {
MCRTransactionManager.rollbackTransactions();
} catch (Exception exception) {
LOGGER.error("Error while performing rollback for command '{}'!", command, exception);
}
if (!continueIfOneFails) {
saveQueue(command, null);
}
}
@@ -80,6 +80,7 @@ public static MCRFileUploadBucket getBucket(String bucketID) { | |||
return BUCKET_MAP.get(bucketID); | |||
} | |||
|
|||
@SuppressWarnings("PMD.ExceptionAsFlowControl") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simplified:
public static synchronized MCRFileUploadBucket createBucket(String bucketID,
Map<String, List<String>> parameters,
MCRUploadHandler uploadHandler) throws MCRUploadServerException {
MCRFileUploadBucket bucket = BUCKET_MAP.get(bucketID);
if (bucket == null) {
bucket = new MCRFileUploadBucket(bucketID, parameters, uploadHandler);
BUCKET_MAP.put(bucketID, bucket);
}
return bucket;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to the high merge effort in current feature branches, we have decided to defer the PMD-PullRequests until the feature branches have been merged.
In addition, all PMD-PRs that are still open will go against the main branch. Branch 2024.06.x is only to be stabilised. So could you please rebase this branch to main?! Thanks!
Link to jira.