Skip to content

Commit

Permalink
Make sure settings yml file can be written to prevent file corruption
Browse files Browse the repository at this point in the history
  • Loading branch information
theotherp committed Apr 15, 2024
1 parent f1ffc23 commit 4f04bb8
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 32 deletions.
13 changes: 12 additions & 1 deletion .run/NzbHydraNativeEntrypoint.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,24 @@
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="MAIN_CLASS_NAME" value="org.nzbhydra.NzbHydraNativeEntrypoint" />
<module name="core" />
<option name="PROGRAM_PARAMETERS" value="org.nzbhydra.NzbHydraNative c:\Users\strat\IdeaProjects\nzbhydra2\core\target\spring-aot\main\sources c:\Users\strat\IdeaProjects\nzbhydra2\core\target\spring-aot\main\resources c:\Users\strat\IdeaProjects\nzbhydra2\core\target\spring-aot\main\classes\ org.nzbhydra nzbhydra2" />
<option name="PROGRAM_PARAMETERS"
value="org.nzbhydra.NzbHydra c:\Users\strat\IdeaProjects\nzbhydra2\core\target\spring-aot\main\sources c:\Users\strat\IdeaProjects\nzbhydra2\core\target\spring-aot\main\resources c:\Users\strat\IdeaProjects\nzbhydra2\core\target\spring-aot\main\classes\ org.nzbhydra nzbhydra2"/>
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/core" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.springframework.boot.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<extension name="net.ashald.envfile">
<option name="IS_ENABLED" value="false"/>
<option name="IS_SUBST" value="false"/>
<option name="IS_PATH_MACRO_SUPPORTED" value="false"/>
<option name="IS_IGNORE_MISSING_FILES" value="false"/>
<option name="IS_ENABLE_EXPERIMENTAL_INTEGRATIONS" value="false"/>
<ENTRIES>
<ENTRY IS_ENABLED="true" PARSER="runconfig" IS_EXECUTABLE="false"/>
</ENTRIES>
</extension>
<method v="2">
<option name="Make" enabled="true" />
Expand Down
35 changes: 24 additions & 11 deletions core/src/main/java/org/nzbhydra/config/ConfigReaderWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@ protected void save(File targetFile, String configAsYamlString) {
}
synchronized (Jackson.YAML_MAPPER) {
Failsafe.with(saveRetryPolicy)
.onFailure(new EventListener<ExecutionCompletedEvent<Object>>() {
@Override
public void accept(ExecutionCompletedEvent<Object> event) throws Throwable {
logger.error("Unable to save config", event.getException());
}
})
.run(() -> doWrite(targetFile, configAsYamlString))
.onFailure(new EventListener<ExecutionCompletedEvent<Object>>() {
@Override
public void accept(ExecutionCompletedEvent<Object> event) throws Throwable {
logger.error("Unable to save config", event.getException());
}
})
.run(() -> doWrite(targetFile, configAsYamlString))
;
}
}
Expand All @@ -108,20 +108,33 @@ private void doWrite(File targetFile, String configAsYamlString) throws IOExcept
throw new IOException("Empty YAML");
}


logger.debug(LoggingMarkers.CONFIG_READ_WRITE, "Writing to file {}", targetFile);
try (final FileOutputStream fos = new FileOutputStream(targetFile)) {
File tempFile = new File(targetFile.getParentFile(), targetFile.getName() + ".tmp");
if (tempFile.exists()) {
boolean deleted = tempFile.delete();
if (!deleted) {
logger.error("Error deleting previous temp file {}", tempFile);
throw new RuntimeException("Error deleting previous temp file");
}
}
logger.debug(LoggingMarkers.CONFIG_READ_WRITE, "Writing to file {}", tempFile);
try (final FileOutputStream fos = new FileOutputStream(tempFile)) {
IOUtils.write(configAsYamlString.getBytes(Charsets.UTF_8), fos);
fos.flush();
fos.getFD().sync();
}

try {
BaseConfig baseConfig = Jackson.YAML_MAPPER.readValue(targetFile, BaseConfig.class);
BaseConfig baseConfig = Jackson.YAML_MAPPER.readValue(tempFile, BaseConfig.class);
} catch (IOException e) {
logger.warn("Written target config file corrupted", e);
throw e;
}
try {
Files.move(tempFile.toPath(), targetFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
logger.warn("Error moving temp file {} to target file {}", tempFile, targetFile, e);
throw e;
}
}

/**
Expand Down
42 changes: 22 additions & 20 deletions core/src/main/java/org/nzbhydra/web/ErrorHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,21 +86,21 @@ public class ErrorHandler {
);

@ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
HttpMediaTypeNotAcceptableException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
ServletRequestBindingException.class,
IOException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
MissingServletRequestPartException.class,
BindException.class,
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class})
HttpMediaTypeNotSupportedException.class,
HttpMediaTypeNotAcceptableException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
ServletRequestBindingException.class,
IOException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
MissingServletRequestPartException.class,
BindException.class,
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class})
@ResponseBody
public ResponseEntity<Object> handleConflict(Exception ex, HttpServletRequest request) {
String fullParametersString = "";
Expand All @@ -111,11 +111,14 @@ public ResponseEntity<Object> handleConflict(Exception ex, HttpServletRequest re
}
String requestURI = request.getRequestURI();
HttpStatus status = getStatusForException(ex);
String message = "Unexpected error when client tried to access path " + requestURI + fullParametersString + ". Error message: " + ex.getMessage();
String message = "Unexpected error when client tried to access path " + requestURI + fullParametersString + ". Error message: " + ex.getMessage();
if (EXCEPTIONS_LOG_WITHOUT_STACKTRACE.contains(ex.getClass())) {
logger.warn(message);
} else {
logger.warn("Unexpected error when client tried to access path " + requestURI + fullParametersString, ex);
//Sometimes favicons are not requested properly by the browser
if (!requestURI.contains("favicon")) {
logger.warn("Unexpected error when client tried to access path " + requestURI + fullParametersString, ex);
}
}
Object bodyOfResponse;
List<MediaType> mediaTypes = new ArrayList<>();
Expand Down Expand Up @@ -190,15 +193,14 @@ private List<MediaType> resolveMediaTypes(HttpServletRequest request)
List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerValues);
MediaType.sortBySpecificityAndQuality(mediaTypes);
return mediaTypes;
}
catch (InvalidMediaTypeException ex) {
} catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotAcceptableException(
"Could not parse 'Accept' header " + headerValues + ": " + ex.getMessage());
}
}

@Data
@ReflectionMarker
@ReflectionMarker
@AllArgsConstructor
@NoArgsConstructor
public static class JsonExceptionResponse {
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/resources/changelog.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#@formatter:off
- version: "v6.1.1"
changes:
- type: "fix"
text: "Make sure settings yml file can be written to prevent file corruption"
- version: "v6.1.0"
changes:
- type: "fix"
Expand Down

0 comments on commit 4f04bb8

Please sign in to comment.