From ecaa02ce9b44a0e693accf91f1cdee3bf7b6c0f5 Mon Sep 17 00:00:00 2001 From: Christoph Langguth Date: Wed, 26 Nov 2025 14:19:24 +0100 Subject: [PATCH 1/4] SED-4405 Introduce editorconfig --- .editorconfig | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f830e3b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,62 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +######################################## +# Java +######################################## +[*.java] +indent_style = space +indent_size = 4 +max_line_length = off + +######################################## +# XML (POM files, Spring XML, misc) +######################################## +[*.xml] +indent_style = space +indent_size = 2 + +######################################## +# JSON +######################################## +[*.json] +indent_style = space +indent_size = 2 + +######################################## +# YAML (docker-compose, GitHub actions, configs) +# YAML SHOULD NEVER USE TABS — many parsers reject them +######################################## +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + + +######################################## +# Markdown +# Keep indentation small; used mainly for lists and fenced blocks. +######################################## +[*.md] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false # needed to preserve Markdown hard line breaks + +######################################## +# Properties files +# Key-value pairs; indenting rarely matters but 2 spaces is common. +######################################## +[*.properties] +indent_style = space +indent_size = 2 + +######################################## +# HTML (simple default) +######################################## +[*.html] +indent_style = space +indent_size = 2 From eb7e29d5e4b012e89f277e7d275381bb1624669f Mon Sep 17 00:00:00 2001 From: Christoph Langguth Date: Wed, 26 Nov 2025 14:46:20 +0100 Subject: [PATCH 2/4] SED-4405 Normalize formatting --- .editorconfig | 2 +- build_parameters.json | 32 +- exense-basic-commons/pom.xml | 80 +- .../ch/exense/commons/app/ArgumentParser.java | 60 +- .../ch/exense/commons/app/Configuration.java | 362 +++--- .../java/ch/exense/commons/io/FileHelper.java | 1017 +++++++++-------- .../exense/commons/io/FileWatchService.java | 190 +-- .../java/ch/exense/commons/io/Poller.java | 68 +- .../processes/ExternalJVMLauncher.java | 80 +- .../commons/processes/ForkedJvmBuilder.java | 4 +- .../commons/processes/ManagedProcess.java | 20 +- .../commons/app/ArgumentParserTest.java | 22 +- .../exense/commons/app/ConfigurationTest.java | 128 +-- .../ch/exense/commons/io/FileHelperTest.java | 188 +-- .../commons/io/FileWatchServiceTest.java | 200 ++-- .../java/ch/exense/commons/io/PollerTest.java | 16 +- .../processes/ExternalJVMLauncherTest.java | 76 +- .../commons/processes/ManagedProcessTest.java | 408 +++---- .../processes/RecursiveProcessTest.java | 6 +- .../src/test/resources/logback-test.xml | 18 +- pom.xml | 652 +++++------ 21 files changed, 1831 insertions(+), 1798 deletions(-) diff --git a/.editorconfig b/.editorconfig index f830e3b..343066c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -44,7 +44,7 @@ indent_size = 2 [*.md] indent_style = space indent_size = 2 -trim_trailing_whitespace = false # needed to preserve Markdown hard line breaks +trim_trailing_whitespace = false # needed to preserve Markdown hard line breaks ######################################## # Properties files diff --git a/build_parameters.json b/build_parameters.json index 5bf2987..05de9c7 100644 --- a/build_parameters.json +++ b/build_parameters.json @@ -1,17 +1,17 @@ { - "ACTION": "COMPILE", - "TYPE": "POM", - "FOLDER": ".", - "PARAMETERS": [ - { - "NAME": "DEVELOPMENT", - "URL": "nexus-staging::https://nexus-enterprise-staging.exense.ch/repository/staging-maven/", - "CONFIG": "SkipJavadoc" - }, - { - "NAME": "PRODUCTION", - "URL": "sonatype::https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/", - "CONFIG": "SignedBuild" - } - ] -} \ No newline at end of file + "ACTION": "COMPILE", + "TYPE": "POM", + "FOLDER": ".", + "PARAMETERS": [ + { + "NAME": "DEVELOPMENT", + "URL": "nexus-staging::https://nexus-enterprise-staging.exense.ch/repository/staging-maven/", + "CONFIG": "SkipJavadoc" + }, + { + "NAME": "PRODUCTION", + "URL": "sonatype::https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/", + "CONFIG": "SignedBuild" + } + ] +} diff --git a/exense-basic-commons/pom.xml b/exense-basic-commons/pom.xml index a458a96..9eff3ca 100644 --- a/exense-basic-commons/pom.xml +++ b/exense-basic-commons/pom.xml @@ -1,45 +1,45 @@ - 4.0.0 - - exense-basic-commons - jar + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - ${project.groupId}:${project.artifactId} - - - ch.exense.commons - exense-commons - 0.0.0-SNAPSHOT - - - - - org.slf4j - slf4j-api - + exense-basic-commons + jar - - ch.qos.logback - logback-classic - test - - + ${project.groupId}:${project.artifactId} - - - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - - - + + ch.exense.commons + exense-commons + 0.0.0-SNAPSHOT + + + + + org.slf4j + slf4j-api + + + + ch.qos.logback + logback-classic + test + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + diff --git a/exense-basic-commons/src/main/java/ch/exense/commons/app/ArgumentParser.java b/exense-basic-commons/src/main/java/ch/exense/commons/app/ArgumentParser.java index 6d460bc..5fbaf29 100644 --- a/exense-basic-commons/src/main/java/ch/exense/commons/app/ArgumentParser.java +++ b/exense-basic-commons/src/main/java/ch/exense/commons/app/ArgumentParser.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -24,31 +24,31 @@ public class ArgumentParser { - private HashMap options = new HashMap(); - private Pattern p = Pattern.compile("-(.+?)(?:=(.+?))?$"); - - public ArgumentParser(String[] paramArrayOfString) { - Matcher localMatcher = null; - for (int i = 0; i < paramArrayOfString.length; i++) { - if (!(localMatcher = this.p.matcher(paramArrayOfString[i])).find()) - continue; - this.options.put(localMatcher.group(1).toLowerCase(), localMatcher.group(2)); - } - } - - public boolean hasOption(String paramString) { - return this.options.containsKey(paramString.toLowerCase()); - } - - public String getOption(String paramString) { - return (String) this.options.get(paramString.toLowerCase()); - } - - public Set> entrySet() { - return options.entrySet(); - } - - public Map getOptions() { - return options; - } + private HashMap options = new HashMap(); + private Pattern p = Pattern.compile("-(.+?)(?:=(.+?))?$"); + + public ArgumentParser(String[] paramArrayOfString) { + Matcher localMatcher = null; + for (int i = 0; i < paramArrayOfString.length; i++) { + if (!(localMatcher = this.p.matcher(paramArrayOfString[i])).find()) + continue; + this.options.put(localMatcher.group(1).toLowerCase(), localMatcher.group(2)); + } + } + + public boolean hasOption(String paramString) { + return this.options.containsKey(paramString.toLowerCase()); + } + + public String getOption(String paramString) { + return (String) this.options.get(paramString.toLowerCase()); + } + + public Set> entrySet() { + return options.entrySet(); + } + + public Map getOptions() { + return options; + } } diff --git a/exense-basic-commons/src/main/java/ch/exense/commons/app/Configuration.java b/exense-basic-commons/src/main/java/ch/exense/commons/app/Configuration.java index c94c715..5443c14 100644 --- a/exense-basic-commons/src/main/java/ch/exense/commons/app/Configuration.java +++ b/exense-basic-commons/src/main/java/ch/exense/commons/app/Configuration.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -35,187 +35,187 @@ public class Configuration implements Closeable { - private static final Logger logger = LoggerFactory.getLogger(Configuration.class); - - private FileWatchService fileWatchService; - - private File propertyFile; - - private Properties properties; - - private Map placeholders; - - public Configuration() { - super(); - properties = new Properties(); - } - - public Configuration(File propertyFile) throws IOException { - this(propertyFile, new HashMap<>()); - } - - public Configuration(File propertyFile, Map placeholders) throws IOException { - super(); - - this.propertyFile = propertyFile; - this.placeholders = placeholders; - - load(); - - if (getPropertyAsBoolean("conf.scan", false)) { - fileWatchService = new FileWatchService(); - fileWatchService.register(propertyFile, new Runnable() { - @Override - public void run() { - try { - load(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }); - } - } - - public void load() throws FileNotFoundException, IOException { - properties = new Properties(); - if (propertyFile != null) { - String propertiesContent = new String(Files.readAllBytes(propertyFile.toPath())); - String resolvedPropertiesContent = replacePlaceholders(propertiesContent); - properties.load(new StringReader(resolvedPropertiesContent)); - } - } - - private String replacePlaceholders(String configXml) { - StringBuffer sb = new StringBuffer(); - Matcher m = Pattern.compile("\\$\\{(.+?)\\}").matcher(configXml); - while (m.find()) { + private static final Logger logger = LoggerFactory.getLogger(Configuration.class); + + private FileWatchService fileWatchService; + + private File propertyFile; + + private Properties properties; + + private Map placeholders; + + public Configuration() { + super(); + properties = new Properties(); + } + + public Configuration(File propertyFile) throws IOException { + this(propertyFile, new HashMap<>()); + } + + public Configuration(File propertyFile, Map placeholders) throws IOException { + super(); + + this.propertyFile = propertyFile; + this.placeholders = placeholders; + + load(); + + if (getPropertyAsBoolean("conf.scan", false)) { + fileWatchService = new FileWatchService(); + fileWatchService.register(propertyFile, new Runnable() { + @Override + public void run() { + try { + load(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + } + + public void load() throws FileNotFoundException, IOException { + properties = new Properties(); + if (propertyFile != null) { + String propertiesContent = new String(Files.readAllBytes(propertyFile.toPath())); + String resolvedPropertiesContent = replacePlaceholders(propertiesContent); + properties.load(new StringReader(resolvedPropertiesContent)); + } + } + + private String replacePlaceholders(String configXml) { + StringBuffer sb = new StringBuffer(); + Matcher m = Pattern.compile("\\$\\{(.+?)\\}").matcher(configXml); + while (m.find()) { String key = m.group(1); - if(placeholders!=null) { - String replacement = placeholders.get(key.toLowerCase()); - if(replacement == null) { - throw new RuntimeException("Missing placeholder '"+key+"'."); - } else { - m.appendReplacement(sb, Matcher.quoteReplacement(replacement)); - } + if (placeholders != null) { + String replacement = placeholders.get(key.toLowerCase()); + if (replacement == null) { + throw new RuntimeException("Missing placeholder '" + key + "'."); + } else { + m.appendReplacement(sb, Matcher.quoteReplacement(replacement)); + } } else { - throw new RuntimeException("Unable to replace placeholders. Placeholder map is null. This should never occur."); + throw new RuntimeException("Unable to replace placeholders. Placeholder map is null. This should never occur."); } } - m.appendTail(sb); - return sb.toString(); - } - - public Properties getUnderlyingPropertyObject(){ - return this.properties; - } - - public String getProperty(String name) { - return properties.getProperty(name); - } - - public String getProperty(String name, String defaultValue) { - return properties.getProperty(name, defaultValue); - } - - public void putProperty(String name, String value) { - properties.put(name, value); - } - - public Integer getPropertyAsInteger(String name) { - return getPropertyAsInteger(name, null); - } - - public Integer getPropertyAsInteger(String name, Integer defaultValue) { - String prop = properties.getProperty(name); - if (prop != null) { - return Integer.parseInt(prop); - } else { - return defaultValue; - } - } - - public Long getPropertyAsLong(String name) { - return getPropertyAsLong(name, null); - } - - public Long getPropertyAsLong(String name, Long defaultValue) { - String prop = properties.getProperty(name); - if (prop != null) { - return Long.parseLong(prop); - } else { - return defaultValue; - } - } - - public boolean getPropertyAsBoolean(String name) { - return getPropertyAsBoolean(name, false); - } - - public boolean hasProperty(String name) { - return properties.containsKey(name); - } - - public boolean getPropertyAsBoolean(String name, boolean defaultValue) { - String prop = properties.getProperty(name); - if (prop != null) { - return Boolean.parseBoolean(prop); - } else { - return defaultValue; - } - } - - public File getPropertyAsFile(String name) { - return getPropertyAsFile(name, null); - } - - public File getPropertyAsFile(String name, File defaultValue) { - String prop = properties.getProperty(name); - if (prop != null) { - return new File(prop); - } else { - return defaultValue; - } - } - - public File getPropertyAsDirectory(String name) { - return getPropertyAsFile(name, null); - } - - public File getPropertyAsDirectory(String name, File defaultValue) { - String prop = properties.getProperty(name); - File file; - if (prop != null) { - file = new File(prop); - } else { - file = defaultValue; - } - if(!file.exists()) { - file.mkdirs(); - } - return file; - } - - public File getPropertyFile() { - return propertyFile; - } - - /** - * @return the {@link Set} of property names (keys) contained in this {@link Configuration} - */ - public Set getPropertyNames() { - return properties.keySet(); - } - - public Map getPlaceholders() { - return placeholders; - } - - @Override - public void close() throws IOException { - if (fileWatchService != null) { - fileWatchService.close(); - } - } + m.appendTail(sb); + return sb.toString(); + } + + public Properties getUnderlyingPropertyObject() { + return this.properties; + } + + public String getProperty(String name) { + return properties.getProperty(name); + } + + public String getProperty(String name, String defaultValue) { + return properties.getProperty(name, defaultValue); + } + + public void putProperty(String name, String value) { + properties.put(name, value); + } + + public Integer getPropertyAsInteger(String name) { + return getPropertyAsInteger(name, null); + } + + public Integer getPropertyAsInteger(String name, Integer defaultValue) { + String prop = properties.getProperty(name); + if (prop != null) { + return Integer.parseInt(prop); + } else { + return defaultValue; + } + } + + public Long getPropertyAsLong(String name) { + return getPropertyAsLong(name, null); + } + + public Long getPropertyAsLong(String name, Long defaultValue) { + String prop = properties.getProperty(name); + if (prop != null) { + return Long.parseLong(prop); + } else { + return defaultValue; + } + } + + public boolean getPropertyAsBoolean(String name) { + return getPropertyAsBoolean(name, false); + } + + public boolean hasProperty(String name) { + return properties.containsKey(name); + } + + public boolean getPropertyAsBoolean(String name, boolean defaultValue) { + String prop = properties.getProperty(name); + if (prop != null) { + return Boolean.parseBoolean(prop); + } else { + return defaultValue; + } + } + + public File getPropertyAsFile(String name) { + return getPropertyAsFile(name, null); + } + + public File getPropertyAsFile(String name, File defaultValue) { + String prop = properties.getProperty(name); + if (prop != null) { + return new File(prop); + } else { + return defaultValue; + } + } + + public File getPropertyAsDirectory(String name) { + return getPropertyAsFile(name, null); + } + + public File getPropertyAsDirectory(String name, File defaultValue) { + String prop = properties.getProperty(name); + File file; + if (prop != null) { + file = new File(prop); + } else { + file = defaultValue; + } + if (!file.exists()) { + file.mkdirs(); + } + return file; + } + + public File getPropertyFile() { + return propertyFile; + } + + /** + * @return the {@link Set} of property names (keys) contained in this {@link Configuration} + */ + public Set getPropertyNames() { + return properties.keySet(); + } + + public Map getPlaceholders() { + return placeholders; + } + + @Override + public void close() throws IOException { + if (fileWatchService != null) { + fileWatchService.close(); + } + } } diff --git a/exense-basic-commons/src/main/java/ch/exense/commons/io/FileHelper.java b/exense-basic-commons/src/main/java/ch/exense/commons/io/FileHelper.java index 2cb5545..1a046fc 100644 --- a/exense-basic-commons/src/main/java/ch/exense/commons/io/FileHelper.java +++ b/exense-basic-commons/src/main/java/ch/exense/commons/io/FileHelper.java @@ -1,6 +1,6 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at @@ -35,498 +35,527 @@ public class FileHelper { - private static final Logger logger = LoggerFactory.getLogger(FileHelper.class); - - public static final int DEFAULT_BUFFER_SIZE = 2048; - - /** - * Creates a temporary file that will be deleted on JVM exit - * @return the {@link File} of the temporary file - * @throws IOException if an error occurs during file creation - */ - public static File createTempFile() throws IOException { - File file = Files.createTempFile(null, null).toFile(); - file.deleteOnExit(); - return file; - } - - /** - * Creates a temporary folder - * @throws IOException if an error occurs during folder creation - */ - public static File createTempFolder() throws IOException { - return createTempFolder(null); - } - - /** - * Creates a temporary folder - * @param prefix the prefix string to be used in generating the folder's name - * @return the {@link File} of the temporary folder - * @throws IOException if an error occurs during folder creation - */ - public static File createTempFolder(String prefix) throws IOException { - return Files.createTempDirectory(prefix).toFile(); - } - - /** - * Deletes a folder recursively - * @param folder the {@link File} to be deleted - */ - public static boolean deleteFolder(File folder) { - File[] files = folder.listFiles(); - if (files != null) { - for (File f : files) { - if (f.isDirectory()) { - deleteFolder(f); - } else { - if (!f.delete()) { - logger.warn("Could not delete file '"+f.getAbsolutePath()+"'"); - } - } - } - } - - boolean deleted = folder.delete(); - if (!deleted) { - logger.warn("Could not delete folder '"+folder.getAbsolutePath()+"'"); - } - return deleted; - } - - /** - * Deletes a folder recursively making sure the folder and his content can safely be deleted beforehand - * @param folder the {@link File} to be deleted - */ - public static boolean safeDeleteFolder(File folder) { - return isFolderDeletable(folder) && deleteFolder(folder); - } - - private static boolean isFolderDeletable(File folder) { - return folder.renameTo(folder); - } - - /** - * Deletes a folder recursively, retrying in case of error - * @param folder the {@link File} to be deleted - */ - public static void deleteFolderWithRetryOnError(File folder) { - File[] files = folder.listFiles(); - // delete folder contents - if (files != null) { - for (File f : files) { - if (f.isDirectory()) { - deleteFolderWithRetryOnError(f); - } else { - deleteFileWithRetryOnError(f); - } - } - } - // delete folder itself - deleteFileWithRetryOnError(folder); - } - - private static void deleteFileWithRetryOnError(File f) { - try { - Poller.waitFor(f::delete, 10_000); - } catch (TimeoutException e) { - //Final try, logging actual exception in case of error - try { - Files.delete(f.toPath()); - } catch (IOException ex) { - logger.error("Unable to delete file " + f.getAbsolutePath(), ex); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - /** - * Requests that the directory denoted by this abstract pathname be deleted recursively when the virtual machine terminates - * @param folder the {@link File} to be deleted - */ - public static void deleteFolderOnExit(File folder) { - folder.deleteOnExit(); - File[] files = folder.listFiles(); - if (files != null) { - for (File f : files) { - if (f.isDirectory()) { - deleteFolderOnExit(f); - } else { - f.deleteOnExit(); - } - } - } - } - - /** - * Computes the last modification date of a file or a folder recursively - * @param file: the file - * @return the time that the file was last modified. - */ - public static long getLastModificationDateRecursive(File file) { - return computeLastModification(file); - } - - protected static long computeLastModification(File file) { - return computeLastModificationDateRecursive(file); - } - - protected static long computeLastModificationDateRecursive(File file) { - if (file.isDirectory()) { - long lastModificationDate = file.lastModified(); - for (File f : Objects.requireNonNull(file.listFiles())) { - long lastChange = computeLastModificationDateRecursive(f); - if (lastChange > lastModificationDate) { - lastModificationDate = lastChange; - } - } - return lastModificationDate; - } else { - return file.lastModified(); - } - } - - /** - * Extracts the zip file to the target folder provided as argument - * @param zipFile the zip file to be extracted - * @param target the target folder to extract to - * @throws IOException if an error occurs during file unzip - */ - public static void unzip(File zipFile, File target) throws IOException { - try (FileInputStream in = new FileInputStream(zipFile)) { - unzip(in, target); - } - } - - - /** - * Extracts the zip provided as byte array to the target folder provided as argument - * @param bytes the byte array of the zip to be extracted - * @param target the target folder to extract to - * @throws IOException if an error occurs during file unzip - */ - public static void unzip(byte[] bytes, File target) throws IOException { - unzip(new ByteArrayInputStream(bytes), target); - } - - /** - * Extracts the zip provided as stream to the target folder provided as argument - * @param stream the {@link InputStream} of the zip to be extracted - * @param target the target folder to extract to - * @throws IOException if an error occurs during file unzip - */ - public static void unzip(InputStream stream, File target) throws IOException { - try (ZipInputStream zip = new ZipInputStream(stream)){ - if (!target.exists()) { - Files.createDirectory(target.toPath()); - } else if (!target.isDirectory()) { - throw new IOException("The target should be a directory"); - } - - ZipEntry entry; - while ((entry = zip.getNextEntry()) != null) { - String currentEntry = entry.getName().replaceAll("\\\\","/"); - - File destFile = new File(target.getAbsolutePath(), currentEntry); - File destinationParent = destFile.getParentFile(); - - Files.createDirectories(destinationParent.toPath()); - - if (!entry.isDirectory()) { - byte[] data = new byte[DEFAULT_BUFFER_SIZE]; - - FileOutputStream fos = new FileOutputStream(destFile); - BufferedOutputStream dest = new BufferedOutputStream(fos, DEFAULT_BUFFER_SIZE); - - int currentByte; - while ((currentByte = zip.read(data, 0, DEFAULT_BUFFER_SIZE)) != -1) { - dest.write(data, 0, currentByte); - } - dest.flush(); - dest.close(); - } - } - } - } - - /** - * Extracts zip entry to file - * @param stream the {@link InputStream} of the zip to be extracted - * @return the extracted file - * @throws IOException if an error occurs during file unzip - */ - public static File unzip(InputStream stream, String entryName) throws IOException { - File f = null; - try (ZipInputStream zip = new ZipInputStream(stream)){ - ZipEntry entry; - while ((entry = zip.getNextEntry()) != null) { - if (!entry.isDirectory() && entry.getName().equals(entryName)) { - f = FileHelper.createTempFile(); - byte[] data = new byte[DEFAULT_BUFFER_SIZE]; - FileOutputStream fos = new FileOutputStream(f); - BufferedOutputStream dest = new BufferedOutputStream(fos, DEFAULT_BUFFER_SIZE); - int currentByte; - while ((currentByte = zip.read(data, 0, DEFAULT_BUFFER_SIZE)) != -1) { - dest.write(data, 0, currentByte); - } - dest.flush(); - dest.close(); - } - } - } - return f; - } - /** - * Create a zip file of the directory denoted by the {@link File} passed as argument - * @param directory the directory to be zipped - * @param target the path to the target zip file - * @throws IOException if an error occurs during file zip - */ - public static void zip(File directory, File target) throws IOException { - FileOutputStream fileOutputStream = new FileOutputStream(target); - zip(directory, fileOutputStream); - fileOutputStream.close(); - } - - /** - * Create a zip file of the directory denoted by the {@link File} passed as argument - * @param directory the directory to be zipped - * @param target the path to the target zip file - * @param fileFilter filter for files or directories in directory - * @throws IOException if an error occurs during file zip - */ - public static void zip(File directory, File target, Function fileFilter) throws IOException { - FileOutputStream fileOutputStream = new FileOutputStream(target); - zip(directory, fileOutputStream, fileFilter); - fileOutputStream.close(); - } - - /** - * Create a zip file of the directory denoted by the {@link File} passed as argument - * @param directory the directory to be zipped - * @param out the output stream of the target zip file - * @throws IOException if an error occurs during file zip - */ - public static void zip(File directory, OutputStream out) throws IOException { - ZipOutputStream zos = new ZipOutputStream(out); - zos.setLevel(ZipOutputStream.STORED); - zip(directory, directory, zos, null); - zos.close(); - } - - /** - * Create a zip file of the directory denoted by the {@link File} passed as argument - * @param directory the directory to be zipped - * @param out the output stream of the target zip file - * @param fileFilter filter for files or directories in directory - * @throws IOException if an error occurs during file zip - */ - public static void zip(File directory, OutputStream out, Function fileFilter) throws IOException { - ZipOutputStream zos = new ZipOutputStream(out); - zos.setLevel(ZipOutputStream.STORED); - zip(directory, directory, zos, fileFilter); - zos.close(); - } - - /** - * Create a zip file of the directory denoted by the {@link File} passed as argument - * @param directory the directory to be zipped - * @return the byte array of the target zip - * @throws IOException if an error occurs during file zip - */ - public static byte[] zip(File directory) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - zip(directory, out, null); - return out.toByteArray(); - } - - private static void zip(File directory, File base, ZipOutputStream zos, Function fileFilter) throws IOException { - File[] files = directory.listFiles(); - byte[] buffer = new byte[8192]; - int read = 0; - for (File file : Objects.requireNonNull(files)) { - if (fileFilter == null || Boolean.TRUE.equals(fileFilter.apply(file))) { - if (file.isDirectory()) { - // ZipOutputStream can handle directories by adding a forward-slash / after the folder name - ZipEntry dirEntry = new ZipEntry(createZipEntryName(base, file) + "/"); - zos.putNextEntry(dirEntry); - zip(file, base, zos, fileFilter); - } else { - FileInputStream in = new FileInputStream(file); - ZipEntry entry = new ZipEntry(createZipEntryName(base, file)); - zos.putNextEntry(entry); - while (-1 != (read = in.read(buffer))) { - zos.write(buffer, 0, read); - } - in.close(); - } - } - } - } - - private static String createZipEntryName(File base, File file) { - return file.getPath().substring(base.getPath().length() + 1).replaceAll("\\\\", "/"); - } - - /** - * Add provided file as byte array to the zip output stream - * @param zos zip output stream - * @param jsonStream bytearray output stream to be added to the zip - * @param name of the zip entry - */ - public static void zipFile(ZipOutputStream zos, ByteArrayOutputStream jsonStream, String name) throws IOException { - ZipEntry entryJson = new ZipEntry(name.replaceAll("\\\\","/")); - zos.putNextEntry(entryJson); - zos.write(jsonStream.toByteArray()); - } - - /** - * Add provided file to the zip output stream removing the base path - * @param zos zip output stream - * @param file to be added to the zip - * @param basePath path of the file to be removed from the zip entry - */ - public static void zipFile(ZipOutputStream zos, File file, String basePath) { - try { - byte[] buffer = new byte[8192]; - int read = 0; - FileInputStream in = new FileInputStream(file); - ZipEntry entry = new ZipEntry(file.getPath().substring(basePath.length() + 1).replaceAll("\\\\","/")); - zos.putNextEntry(entry); - while (-1 != (read = in.read(buffer))) { - zos.write(buffer, 0, read); - } - in.close(); - } catch (IOException e) { - throw new RuntimeException("Unable to create archive",e); - } - } - - /** - * Check whether the provided file is an archive - * @return the check result - */ - public static boolean isArchive(File f) { - int fileSignature = 0; - try (RandomAccessFile raf = new RandomAccessFile(f, "r")) { - fileSignature = raf.readInt(); - } catch (IOException e) { - // handle if you like - } - return fileSignature == 0x504B0304 || fileSignature == 0x504B0506 || fileSignature == 0x504B0708; - } - - /** - * Reads the stream provided as argument using UTF8 charset - * @param is the stream to be read - * @return the content of the stream - */ - public static String readStream(InputStream is) { - try (Scanner scanner = new Scanner(is, StandardCharsets.UTF_8)) { - return scanner.useDelimiter("\\A").next().replaceAll("\r\n", "\n"); - } - } - - /** - * Reads a resource using UTF8 charset - * @param clazz the class the resource is associated with - * @param resourceName the name of the resource - * @return the content of the resource - */ - public static String readResource(Class clazz, String resourceName) { - return readStream(clazz.getResourceAsStream(resourceName)); - } - - /** - * Reads a classloader resource using UTF8 charset - * @param classLoader the classloader to access the resource - * @param resourceName the name of the resource - * @return the content of the resource - */ - public static String readClassLoaderResource(ClassLoader classLoader, String resourceName) { - return readStream(classLoader.getResourceAsStream(resourceName)); - } - - /** - * Reads a resource and returns its content as byte array - * @param clazz the class the resource is associated with - * @param resourceName the name of the resource - * @return the content of the resource as byte array - * @throws IOException if an error occurs when reading resource - */ - public static byte[] readResourceAsByteArray(Class clazz, String resourceName) throws IOException { - try(InputStream resourceAsStream = clazz.getResourceAsStream(resourceName); ByteArrayOutputStream out = new ByteArrayOutputStream()) { - copy(resourceAsStream, out); - return out.toByteArray(); - } - } - - /** - * Reads a classloader resource and returns its content as byte array - * @param classLoader the classloader to access the resource - * @param resourceName the name of the resource - * @return the content of the resource as byte array - * @throws IOException if an error occurs when reading resource - */ - public static byte[] readClassLoaderResourceAsByteArray(ClassLoader classLoader, String resourceName) throws IOException { - try(InputStream resourceAsStream = classLoader.getResourceAsStream(resourceName); ByteArrayOutputStream out = new ByteArrayOutputStream()) { - copy(resourceAsStream, out); - return out.toByteArray(); - } - } - - /** - * @param classLoader the classloader to access the resource - * @param resourceName the name of the resource - * @return the resource as {@link File} - */ - public static File getClassLoaderResourceAsFile(ClassLoader classLoader, String resourceName) { - try { - URL url = classLoader.getResource(resourceName); - // workaround: doing toURI().getPath() to decode %20 in case of spaces in path - return url != null ? new File(url.toURI().getPath()) : null; - } catch (URISyntaxException e) { - throw new RuntimeException("Error while parsing URI of resource " + resourceName, e); - } - } - - /** - * Extract a resource and copy it to a a temporary file - * @param clazz the class the resource is associated with - * @param resourceName the name of the resource - * @return the temporary {@link File} - * @throws IOException if an error occurs during resource extraction - */ - public static File extractResourceToTempFile(Class clazz, String resourceName) throws IOException { - Path tempFile = Files.createTempFile("resourceName",".tmp"); - Files.write(tempFile, readResource(clazz, resourceName).getBytes(), StandardOpenOption.APPEND); - tempFile.toFile().deleteOnExit(); - return tempFile.toFile(); - } - - /** - * Copy a {@link InputStream} to an {@link OutputStream} using a buffer size of 2048 - * @param input the {@link InputStream} to be read - * @param output the target {@link OutputStream} - * @throws IOException if an error occurs during copy - */ - public static void copy(final InputStream input, final OutputStream output) throws IOException { - copy(input, output, 2048); - } - - /** - * Copy a {@link InputStream} to an {@link OutputStream} - * @param input the {@link InputStream} to be read - * @param output the target {@link OutputStream} - * @param bufferSize the buffer size to be used - * @throws IOException if an error occurs during copy - */ - public static void copy(final InputStream input, final OutputStream output, int bufferSize) throws IOException { - final byte[] buffer = new byte[bufferSize]; - int n = 0; - while ((n = input.read(buffer)) > 0) { - output.write(buffer, 0, n); - } - } + private static final Logger logger = LoggerFactory.getLogger(FileHelper.class); + + public static final int DEFAULT_BUFFER_SIZE = 2048; + + /** + * Creates a temporary file that will be deleted on JVM exit + * + * @return the {@link File} of the temporary file + * @throws IOException if an error occurs during file creation + */ + public static File createTempFile() throws IOException { + File file = Files.createTempFile(null, null).toFile(); + file.deleteOnExit(); + return file; + } + + /** + * Creates a temporary folder + * + * @throws IOException if an error occurs during folder creation + */ + public static File createTempFolder() throws IOException { + return createTempFolder(null); + } + + /** + * Creates a temporary folder + * + * @param prefix the prefix string to be used in generating the folder's name + * @return the {@link File} of the temporary folder + * @throws IOException if an error occurs during folder creation + */ + public static File createTempFolder(String prefix) throws IOException { + return Files.createTempDirectory(prefix).toFile(); + } + + /** + * Deletes a folder recursively + * + * @param folder the {@link File} to be deleted + */ + public static boolean deleteFolder(File folder) { + File[] files = folder.listFiles(); + if (files != null) { + for (File f : files) { + if (f.isDirectory()) { + deleteFolder(f); + } else { + if (!f.delete()) { + logger.warn("Could not delete file '" + f.getAbsolutePath() + "'"); + } + } + } + } + + boolean deleted = folder.delete(); + if (!deleted) { + logger.warn("Could not delete folder '" + folder.getAbsolutePath() + "'"); + } + return deleted; + } + + /** + * Deletes a folder recursively making sure the folder and his content can safely be deleted beforehand + * + * @param folder the {@link File} to be deleted + */ + public static boolean safeDeleteFolder(File folder) { + return isFolderDeletable(folder) && deleteFolder(folder); + } + + private static boolean isFolderDeletable(File folder) { + return folder.renameTo(folder); + } + + /** + * Deletes a folder recursively, retrying in case of error + * + * @param folder the {@link File} to be deleted + */ + public static void deleteFolderWithRetryOnError(File folder) { + File[] files = folder.listFiles(); + // delete folder contents + if (files != null) { + for (File f : files) { + if (f.isDirectory()) { + deleteFolderWithRetryOnError(f); + } else { + deleteFileWithRetryOnError(f); + } + } + } + // delete folder itself + deleteFileWithRetryOnError(folder); + } + + private static void deleteFileWithRetryOnError(File f) { + try { + Poller.waitFor(f::delete, 10_000); + } catch (TimeoutException e) { + //Final try, logging actual exception in case of error + try { + Files.delete(f.toPath()); + } catch (IOException ex) { + logger.error("Unable to delete file " + f.getAbsolutePath(), ex); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + /** + * Requests that the directory denoted by this abstract pathname be deleted recursively when the virtual machine terminates + * + * @param folder the {@link File} to be deleted + */ + public static void deleteFolderOnExit(File folder) { + folder.deleteOnExit(); + File[] files = folder.listFiles(); + if (files != null) { + for (File f : files) { + if (f.isDirectory()) { + deleteFolderOnExit(f); + } else { + f.deleteOnExit(); + } + } + } + } + + /** + * Computes the last modification date of a file or a folder recursively + * + * @param file: the file + * @return the time that the file was last modified. + */ + public static long getLastModificationDateRecursive(File file) { + return computeLastModification(file); + } + + protected static long computeLastModification(File file) { + return computeLastModificationDateRecursive(file); + } + + protected static long computeLastModificationDateRecursive(File file) { + if (file.isDirectory()) { + long lastModificationDate = file.lastModified(); + for (File f : Objects.requireNonNull(file.listFiles())) { + long lastChange = computeLastModificationDateRecursive(f); + if (lastChange > lastModificationDate) { + lastModificationDate = lastChange; + } + } + return lastModificationDate; + } else { + return file.lastModified(); + } + } + + /** + * Extracts the zip file to the target folder provided as argument + * + * @param zipFile the zip file to be extracted + * @param target the target folder to extract to + * @throws IOException if an error occurs during file unzip + */ + public static void unzip(File zipFile, File target) throws IOException { + try (FileInputStream in = new FileInputStream(zipFile)) { + unzip(in, target); + } + } + + + /** + * Extracts the zip provided as byte array to the target folder provided as argument + * + * @param bytes the byte array of the zip to be extracted + * @param target the target folder to extract to + * @throws IOException if an error occurs during file unzip + */ + public static void unzip(byte[] bytes, File target) throws IOException { + unzip(new ByteArrayInputStream(bytes), target); + } + + /** + * Extracts the zip provided as stream to the target folder provided as argument + * + * @param stream the {@link InputStream} of the zip to be extracted + * @param target the target folder to extract to + * @throws IOException if an error occurs during file unzip + */ + public static void unzip(InputStream stream, File target) throws IOException { + try (ZipInputStream zip = new ZipInputStream(stream)) { + if (!target.exists()) { + Files.createDirectory(target.toPath()); + } else if (!target.isDirectory()) { + throw new IOException("The target should be a directory"); + } + + ZipEntry entry; + while ((entry = zip.getNextEntry()) != null) { + String currentEntry = entry.getName().replaceAll("\\\\", "/"); + + File destFile = new File(target.getAbsolutePath(), currentEntry); + File destinationParent = destFile.getParentFile(); + + Files.createDirectories(destinationParent.toPath()); + + if (!entry.isDirectory()) { + byte[] data = new byte[DEFAULT_BUFFER_SIZE]; + + FileOutputStream fos = new FileOutputStream(destFile); + BufferedOutputStream dest = new BufferedOutputStream(fos, DEFAULT_BUFFER_SIZE); + + int currentByte; + while ((currentByte = zip.read(data, 0, DEFAULT_BUFFER_SIZE)) != -1) { + dest.write(data, 0, currentByte); + } + dest.flush(); + dest.close(); + } + } + } + } + + /** + * Extracts zip entry to file + * + * @param stream the {@link InputStream} of the zip to be extracted + * @return the extracted file + * @throws IOException if an error occurs during file unzip + */ + public static File unzip(InputStream stream, String entryName) throws IOException { + File f = null; + try (ZipInputStream zip = new ZipInputStream(stream)) { + ZipEntry entry; + while ((entry = zip.getNextEntry()) != null) { + if (!entry.isDirectory() && entry.getName().equals(entryName)) { + f = FileHelper.createTempFile(); + byte[] data = new byte[DEFAULT_BUFFER_SIZE]; + FileOutputStream fos = new FileOutputStream(f); + BufferedOutputStream dest = new BufferedOutputStream(fos, DEFAULT_BUFFER_SIZE); + int currentByte; + while ((currentByte = zip.read(data, 0, DEFAULT_BUFFER_SIZE)) != -1) { + dest.write(data, 0, currentByte); + } + dest.flush(); + dest.close(); + } + } + } + return f; + } + + /** + * Create a zip file of the directory denoted by the {@link File} passed as argument + * + * @param directory the directory to be zipped + * @param target the path to the target zip file + * @throws IOException if an error occurs during file zip + */ + public static void zip(File directory, File target) throws IOException { + FileOutputStream fileOutputStream = new FileOutputStream(target); + zip(directory, fileOutputStream); + fileOutputStream.close(); + } + + /** + * Create a zip file of the directory denoted by the {@link File} passed as argument + * + * @param directory the directory to be zipped + * @param target the path to the target zip file + * @param fileFilter filter for files or directories in directory + * @throws IOException if an error occurs during file zip + */ + public static void zip(File directory, File target, Function fileFilter) throws IOException { + FileOutputStream fileOutputStream = new FileOutputStream(target); + zip(directory, fileOutputStream, fileFilter); + fileOutputStream.close(); + } + + /** + * Create a zip file of the directory denoted by the {@link File} passed as argument + * + * @param directory the directory to be zipped + * @param out the output stream of the target zip file + * @throws IOException if an error occurs during file zip + */ + public static void zip(File directory, OutputStream out) throws IOException { + ZipOutputStream zos = new ZipOutputStream(out); + zos.setLevel(ZipOutputStream.STORED); + zip(directory, directory, zos, null); + zos.close(); + } + + /** + * Create a zip file of the directory denoted by the {@link File} passed as argument + * + * @param directory the directory to be zipped + * @param out the output stream of the target zip file + * @param fileFilter filter for files or directories in directory + * @throws IOException if an error occurs during file zip + */ + public static void zip(File directory, OutputStream out, Function fileFilter) throws IOException { + ZipOutputStream zos = new ZipOutputStream(out); + zos.setLevel(ZipOutputStream.STORED); + zip(directory, directory, zos, fileFilter); + zos.close(); + } + + /** + * Create a zip file of the directory denoted by the {@link File} passed as argument + * + * @param directory the directory to be zipped + * @return the byte array of the target zip + * @throws IOException if an error occurs during file zip + */ + public static byte[] zip(File directory) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + zip(directory, out, null); + return out.toByteArray(); + } + + private static void zip(File directory, File base, ZipOutputStream zos, Function fileFilter) throws IOException { + File[] files = directory.listFiles(); + byte[] buffer = new byte[8192]; + int read = 0; + for (File file : Objects.requireNonNull(files)) { + if (fileFilter == null || Boolean.TRUE.equals(fileFilter.apply(file))) { + if (file.isDirectory()) { + // ZipOutputStream can handle directories by adding a forward-slash / after the folder name + ZipEntry dirEntry = new ZipEntry(createZipEntryName(base, file) + "/"); + zos.putNextEntry(dirEntry); + zip(file, base, zos, fileFilter); + } else { + FileInputStream in = new FileInputStream(file); + ZipEntry entry = new ZipEntry(createZipEntryName(base, file)); + zos.putNextEntry(entry); + while (-1 != (read = in.read(buffer))) { + zos.write(buffer, 0, read); + } + in.close(); + } + } + } + } + + private static String createZipEntryName(File base, File file) { + return file.getPath().substring(base.getPath().length() + 1).replaceAll("\\\\", "/"); + } + + /** + * Add provided file as byte array to the zip output stream + * + * @param zos zip output stream + * @param jsonStream bytearray output stream to be added to the zip + * @param name of the zip entry + */ + public static void zipFile(ZipOutputStream zos, ByteArrayOutputStream jsonStream, String name) throws IOException { + ZipEntry entryJson = new ZipEntry(name.replaceAll("\\\\", "/")); + zos.putNextEntry(entryJson); + zos.write(jsonStream.toByteArray()); + } + + /** + * Add provided file to the zip output stream removing the base path + * + * @param zos zip output stream + * @param file to be added to the zip + * @param basePath path of the file to be removed from the zip entry + */ + public static void zipFile(ZipOutputStream zos, File file, String basePath) { + try { + byte[] buffer = new byte[8192]; + int read = 0; + FileInputStream in = new FileInputStream(file); + ZipEntry entry = new ZipEntry(file.getPath().substring(basePath.length() + 1).replaceAll("\\\\", "/")); + zos.putNextEntry(entry); + while (-1 != (read = in.read(buffer))) { + zos.write(buffer, 0, read); + } + in.close(); + } catch (IOException e) { + throw new RuntimeException("Unable to create archive", e); + } + } + + /** + * Check whether the provided file is an archive + * + * @return the check result + */ + public static boolean isArchive(File f) { + int fileSignature = 0; + try (RandomAccessFile raf = new RandomAccessFile(f, "r")) { + fileSignature = raf.readInt(); + } catch (IOException e) { + // handle if you like + } + return fileSignature == 0x504B0304 || fileSignature == 0x504B0506 || fileSignature == 0x504B0708; + } + + /** + * Reads the stream provided as argument using UTF8 charset + * + * @param is the stream to be read + * @return the content of the stream + */ + public static String readStream(InputStream is) { + try (Scanner scanner = new Scanner(is, StandardCharsets.UTF_8)) { + return scanner.useDelimiter("\\A").next().replaceAll("\r\n", "\n"); + } + } + + /** + * Reads a resource using UTF8 charset + * + * @param clazz the class the resource is associated with + * @param resourceName the name of the resource + * @return the content of the resource + */ + public static String readResource(Class clazz, String resourceName) { + return readStream(clazz.getResourceAsStream(resourceName)); + } + + /** + * Reads a classloader resource using UTF8 charset + * + * @param classLoader the classloader to access the resource + * @param resourceName the name of the resource + * @return the content of the resource + */ + public static String readClassLoaderResource(ClassLoader classLoader, String resourceName) { + return readStream(classLoader.getResourceAsStream(resourceName)); + } + + /** + * Reads a resource and returns its content as byte array + * + * @param clazz the class the resource is associated with + * @param resourceName the name of the resource + * @return the content of the resource as byte array + * @throws IOException if an error occurs when reading resource + */ + public static byte[] readResourceAsByteArray(Class clazz, String resourceName) throws IOException { + try (InputStream resourceAsStream = clazz.getResourceAsStream(resourceName); ByteArrayOutputStream out = new ByteArrayOutputStream()) { + copy(resourceAsStream, out); + return out.toByteArray(); + } + } + + /** + * Reads a classloader resource and returns its content as byte array + * + * @param classLoader the classloader to access the resource + * @param resourceName the name of the resource + * @return the content of the resource as byte array + * @throws IOException if an error occurs when reading resource + */ + public static byte[] readClassLoaderResourceAsByteArray(ClassLoader classLoader, String resourceName) throws IOException { + try (InputStream resourceAsStream = classLoader.getResourceAsStream(resourceName); ByteArrayOutputStream out = new ByteArrayOutputStream()) { + copy(resourceAsStream, out); + return out.toByteArray(); + } + } + + /** + * @param classLoader the classloader to access the resource + * @param resourceName the name of the resource + * @return the resource as {@link File} + */ + public static File getClassLoaderResourceAsFile(ClassLoader classLoader, String resourceName) { + try { + URL url = classLoader.getResource(resourceName); + // workaround: doing toURI().getPath() to decode %20 in case of spaces in path + return url != null ? new File(url.toURI().getPath()) : null; + } catch (URISyntaxException e) { + throw new RuntimeException("Error while parsing URI of resource " + resourceName, e); + } + } + + /** + * Extract a resource and copy it to a a temporary file + * + * @param clazz the class the resource is associated with + * @param resourceName the name of the resource + * @return the temporary {@link File} + * @throws IOException if an error occurs during resource extraction + */ + public static File extractResourceToTempFile(Class clazz, String resourceName) throws IOException { + Path tempFile = Files.createTempFile("resourceName", ".tmp"); + Files.write(tempFile, readResource(clazz, resourceName).getBytes(), StandardOpenOption.APPEND); + tempFile.toFile().deleteOnExit(); + return tempFile.toFile(); + } + + /** + * Copy a {@link InputStream} to an {@link OutputStream} using a buffer size of 2048 + * + * @param input the {@link InputStream} to be read + * @param output the target {@link OutputStream} + * @throws IOException if an error occurs during copy + */ + public static void copy(final InputStream input, final OutputStream output) throws IOException { + copy(input, output, 2048); + } + + /** + * Copy a {@link InputStream} to an {@link OutputStream} + * + * @param input the {@link InputStream} to be read + * @param output the target {@link OutputStream} + * @param bufferSize the buffer size to be used + * @throws IOException if an error occurs during copy + */ + public static void copy(final InputStream input, final OutputStream output, int bufferSize) throws IOException { + final byte[] buffer = new byte[bufferSize]; + int n = 0; + while ((n = input.read(buffer)) > 0) { + output.write(buffer, 0, n); + } + } } diff --git a/exense-basic-commons/src/main/java/ch/exense/commons/io/FileWatchService.java b/exense-basic-commons/src/main/java/ch/exense/commons/io/FileWatchService.java index 5629a64..a314cba 100644 --- a/exense-basic-commons/src/main/java/ch/exense/commons/io/FileWatchService.java +++ b/exense-basic-commons/src/main/java/ch/exense/commons/io/FileWatchService.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -26,96 +26,96 @@ import java.util.concurrent.ConcurrentHashMap; public class FileWatchService extends Thread implements Closeable { - - private static final Logger logger = LoggerFactory.getLogger(FileWatchService.class); - - private final ConcurrentHashMap subscriptions = new ConcurrentHashMap<>(); - - private int interval = 1000; - - public FileWatchService() { - super(); - - setDaemon(true); - - start(); - } - - public int getInterval() { - return interval; - } - - public void setInterval(int interval) { - this.interval = interval; - } - - @Override - public void run() { - super.run(); - - while (running) { - try { - Thread.sleep(interval); - } catch (InterruptedException e) { - logger.error("Thread interrupted while sleeping", e); - } - - - try { - List> subscriptionsList; - // defensive copy in case a callback itself modifies subscriptions - subscriptionsList = new ArrayList<>(subscriptions.entrySet()); - for (Entry entry : subscriptionsList) { - long lastModificationDate = FileHelper.getLastModificationDateRecursive(entry.getKey()); - logger.trace("Checking for modifications: file={} lastModified={} lastKnownModified={} changed={}", entry.getKey(), lastModificationDate, entry.getValue().lastupdate, lastModificationDate > entry.getValue().lastupdate); - if (lastModificationDate > entry.getValue().lastupdate) { - logger.info("Reloading file: " + entry.getKey().getAbsolutePath()); - entry.getValue().lastupdate = lastModificationDate; - try { - entry.getValue().callback.run(); - } catch (Throwable e) { - logger.error("An error occurred while calling callback for file " + entry.getKey(), e); - } - } - } - } catch (Throwable t) { - logger.error("Unexpected exception", t); - } - } - } - - private static class Subscription { - - long lastupdate; - - Runnable callback; - - public Subscription(long lastupdate, Runnable callback) { - super(); - this.lastupdate = lastupdate; - this.callback = callback; - } - } - - public void register(File file, Runnable callback) { - register(file, callback, false); - } - - public void register(File file, Runnable callback, boolean callOnRegistration) { - logger.debug("Registering file " + file); - subscriptions.put(file, new Subscription(callOnRegistration?0:FileHelper.getLastModificationDateRecursive(file), callback)); - } - - public void unregister(File file) { - logger.debug("Unregistering file " + file); - subscriptions.remove(file); - } - - private volatile boolean running = true; - - @Override - public void close() { - logger.info("Closing and terminating"); - running = false; - } + + private static final Logger logger = LoggerFactory.getLogger(FileWatchService.class); + + private final ConcurrentHashMap subscriptions = new ConcurrentHashMap<>(); + + private int interval = 1000; + + public FileWatchService() { + super(); + + setDaemon(true); + + start(); + } + + public int getInterval() { + return interval; + } + + public void setInterval(int interval) { + this.interval = interval; + } + + @Override + public void run() { + super.run(); + + while (running) { + try { + Thread.sleep(interval); + } catch (InterruptedException e) { + logger.error("Thread interrupted while sleeping", e); + } + + + try { + List> subscriptionsList; + // defensive copy in case a callback itself modifies subscriptions + subscriptionsList = new ArrayList<>(subscriptions.entrySet()); + for (Entry entry : subscriptionsList) { + long lastModificationDate = FileHelper.getLastModificationDateRecursive(entry.getKey()); + logger.trace("Checking for modifications: file={} lastModified={} lastKnownModified={} changed={}", entry.getKey(), lastModificationDate, entry.getValue().lastupdate, lastModificationDate > entry.getValue().lastupdate); + if (lastModificationDate > entry.getValue().lastupdate) { + logger.info("Reloading file: " + entry.getKey().getAbsolutePath()); + entry.getValue().lastupdate = lastModificationDate; + try { + entry.getValue().callback.run(); + } catch (Throwable e) { + logger.error("An error occurred while calling callback for file " + entry.getKey(), e); + } + } + } + } catch (Throwable t) { + logger.error("Unexpected exception", t); + } + } + } + + private static class Subscription { + + long lastupdate; + + Runnable callback; + + public Subscription(long lastupdate, Runnable callback) { + super(); + this.lastupdate = lastupdate; + this.callback = callback; + } + } + + public void register(File file, Runnable callback) { + register(file, callback, false); + } + + public void register(File file, Runnable callback, boolean callOnRegistration) { + logger.debug("Registering file " + file); + subscriptions.put(file, new Subscription(callOnRegistration ? 0 : FileHelper.getLastModificationDateRecursive(file), callback)); + } + + public void unregister(File file) { + logger.debug("Unregistering file " + file); + subscriptions.remove(file); + } + + private volatile boolean running = true; + + @Override + public void close() { + logger.info("Closing and terminating"); + running = false; + } } diff --git a/exense-basic-commons/src/main/java/ch/exense/commons/io/Poller.java b/exense-basic-commons/src/main/java/ch/exense/commons/io/Poller.java index 24cdab0..8278fce 100644 --- a/exense-basic-commons/src/main/java/ch/exense/commons/io/Poller.java +++ b/exense-basic-commons/src/main/java/ch/exense/commons/io/Poller.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -20,34 +20,36 @@ public class Poller { - /** - * Waits for the predicate to return true using a default polling rate of 100ms - * @param predicate the predicate to be tested - * @param timeout the timeout in ms - * @throws TimeoutException - * @throws InterruptedException - */ - public static void waitFor(Supplier predicate, long timeout) throws TimeoutException, InterruptedException { - waitFor(predicate, timeout, 100); - } - - /** - * Waits for the predicate to return true - * @param predicate the predicate to be tested - * @param timeout the timeout in ms - * @param pollingIntervalMs the polling interval in ms - * @throws TimeoutException - * @throws InterruptedException - */ - public static void waitFor(Supplier predicate, long timeout, long pollingIntervalMs) throws TimeoutException, InterruptedException { - long t1 = System.currentTimeMillis(); - while(timeout == 0 || System.currentTimeMillis() predicate, long timeout) throws TimeoutException, InterruptedException { + waitFor(predicate, timeout, 100); + } + + /** + * Waits for the predicate to return true + * + * @param predicate the predicate to be tested + * @param timeout the timeout in ms + * @param pollingIntervalMs the polling interval in ms + * @throws TimeoutException + * @throws InterruptedException + */ + public static void waitFor(Supplier predicate, long timeout, long pollingIntervalMs) throws TimeoutException, InterruptedException { + long t1 = System.currentTimeMillis(); + while (timeout == 0 || System.currentTimeMillis() < t1 + timeout) { + boolean result = predicate.get(); + if (result) { + return; + } + Thread.sleep(pollingIntervalMs); + } + throw new TimeoutException("Timeout (waiting for " + timeout + "ms)"); + } } diff --git a/exense-basic-commons/src/main/java/ch/exense/commons/processes/ExternalJVMLauncher.java b/exense-basic-commons/src/main/java/ch/exense/commons/processes/ExternalJVMLauncher.java index 6e8fccf..aaf5071 100644 --- a/exense-basic-commons/src/main/java/ch/exense/commons/processes/ExternalJVMLauncher.java +++ b/exense-basic-commons/src/main/java/ch/exense/commons/processes/ExternalJVMLauncher.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -25,49 +25,49 @@ public class ExternalJVMLauncher { - private static final Logger logger = LoggerFactory.getLogger(ExternalJVMLauncher.class); - private final String javaPath; - - private final File processLogFolder; + private static final Logger logger = LoggerFactory.getLogger(ExternalJVMLauncher.class); + private final String javaPath; - public ExternalJVMLauncher(String javaPath, File processLogFolder) { - super(); - this.javaPath = javaPath; - this.processLogFolder = processLogFolder; - } + private final File processLogFolder; - public ManagedProcess launchExternalJVM(String name, Class mainClass, List vmargs, List progargs) throws ManagedProcessException { - return launchExternalJVM(name, mainClass, vmargs, progargs, true); - } + public ExternalJVMLauncher(String javaPath, File processLogFolder) { + super(); + this.javaPath = javaPath; + this.processLogFolder = processLogFolder; + } - public ManagedProcess launchExternalJVM(String name, Class mainClass, List vmargs, List progargs, boolean redirectOutput) throws ManagedProcessException { - return launchExternalJVM(name, mainClass.getName(), vmargs, progargs, redirectOutput); - } + public ManagedProcess launchExternalJVM(String name, Class mainClass, List vmargs, List progargs) throws ManagedProcessException { + return launchExternalJVM(name, mainClass, vmargs, progargs, true); + } - public ManagedProcess launchExternalJVM(String name, String mainClass, List vmargs, List progargs, boolean redirectOutput) throws ManagedProcessException { - try { - ForkedJvmBuilder forkedJvmBuilder = new ForkedJvmBuilder(javaPath, mainClass, vmargs, progargs); - ManagedProcess process = new JvmManagedProcess(forkedJvmBuilder, name, processLogFolder, redirectOutput); - process.start(); - return process; - } catch (IOException e) { - throw new ManagedProcessException("Unable to create the java argument file specifying the classpath.",e); - } - } + public ManagedProcess launchExternalJVM(String name, Class mainClass, List vmargs, List progargs, boolean redirectOutput) throws ManagedProcessException { + return launchExternalJVM(name, mainClass.getName(), vmargs, progargs, redirectOutput); + } - private static class JvmManagedProcess extends ManagedProcess { + public ManagedProcess launchExternalJVM(String name, String mainClass, List vmargs, List progargs, boolean redirectOutput) throws ManagedProcessException { + try { + ForkedJvmBuilder forkedJvmBuilder = new ForkedJvmBuilder(javaPath, mainClass, vmargs, progargs); + ManagedProcess process = new JvmManagedProcess(forkedJvmBuilder, name, processLogFolder, redirectOutput); + process.start(); + return process; + } catch (IOException e) { + throw new ManagedProcessException("Unable to create the java argument file specifying the classpath.", e); + } + } - private final ForkedJvmBuilder forkedJvmBuilder; + private static class JvmManagedProcess extends ManagedProcess { - public JvmManagedProcess(ForkedJvmBuilder result, String name, File baseLogDirectory, boolean redirectOutput) throws ManagedProcessException { - super(name, result.getProcessCommand(), baseLogDirectory, redirectOutput); - this.forkedJvmBuilder = result; - } + private final ForkedJvmBuilder forkedJvmBuilder; - @Override - public void close() throws IOException { - super.close(); - forkedJvmBuilder.close(); - } - } + public JvmManagedProcess(ForkedJvmBuilder result, String name, File baseLogDirectory, boolean redirectOutput) throws ManagedProcessException { + super(name, result.getProcessCommand(), baseLogDirectory, redirectOutput); + this.forkedJvmBuilder = result; + } + + @Override + public void close() throws IOException { + super.close(); + forkedJvmBuilder.close(); + } + } } diff --git a/exense-basic-commons/src/main/java/ch/exense/commons/processes/ForkedJvmBuilder.java b/exense-basic-commons/src/main/java/ch/exense/commons/processes/ForkedJvmBuilder.java index 1e9fd6b..2ab4313 100644 --- a/exense-basic-commons/src/main/java/ch/exense/commons/processes/ForkedJvmBuilder.java +++ b/exense-basic-commons/src/main/java/ch/exense/commons/processes/ForkedJvmBuilder.java @@ -41,9 +41,9 @@ protected static File buildClasspath() throws IOException { try (OutputStreamWriter cp = new OutputStreamWriter(new FileOutputStream(javaClassPathArgsFile), StandardCharsets.UTF_8)) { cp.append("-cp "); cp.append("\""); - for(String path:classPathEntries) { + for (String path : classPathEntries) { String absolutePath = new File(path).getCanonicalPath(); - absolutePath = (isWindows()) ? absolutePath.replace("\\","/") : absolutePath; + absolutePath = (isWindows()) ? absolutePath.replace("\\", "/") : absolutePath; cp.append(absolutePath); cp.append(File.pathSeparator); } diff --git a/exense-basic-commons/src/main/java/ch/exense/commons/processes/ManagedProcess.java b/exense-basic-commons/src/main/java/ch/exense/commons/processes/ManagedProcess.java index 0df3bf1..d1a8f47 100644 --- a/exense-basic-commons/src/main/java/ch/exense/commons/processes/ManagedProcess.java +++ b/exense-basic-commons/src/main/java/ch/exense/commons/processes/ManagedProcess.java @@ -132,7 +132,7 @@ public ManagedProcess(String name, List commands, File executionDirector * @param environments list of environment variables to pass to the process */ public ManagedProcess(String name, List commands, File executionDirectory, File baseLogDirectory, - boolean redirectOutput, Map environments) { + boolean redirectOutput, Map environments) { super(); this.environments = environments; @@ -156,7 +156,7 @@ private void createDirectoryIfNotExisting(File directory) { if (!directory.exists()) { if (!directory.mkdirs()) { throw new InvalidParameterException("Unable to create directory for process " + id - + ". Please ensure that the file " + directory.getAbsolutePath() + " is writable."); + + ". Please ensure that the file " + directory.getAbsolutePath() + " is writable."); } } } @@ -197,9 +197,9 @@ public String getProcessErrorLogAsString() { */ public String getProcessLog() { return "The output of the process " + id + " was:\n" + - getProcessOutputLogAsString() + - "The error output of the process " + id + " was:\n" + - getProcessErrorLogAsString(); + getProcessOutputLogAsString() + + "The error output of the process " + id + " was:\n" + + getProcessErrorLogAsString(); } private static String readProcessLog(File file) { @@ -260,7 +260,7 @@ public synchronized void start() throws ManagedProcessException { try { removeTempLogDirectory(); } finally { - if(t instanceof ManagedProcessException) { + if (t instanceof ManagedProcessException) { throw t; } else { throw loggedManagedProcessException("Unexpected error while starting the process " + id, t); @@ -306,7 +306,7 @@ public int waitFor(long timeout) throws TimeoutException, InterruptedException { boolean terminated = process.waitFor(timeout, TimeUnit.MILLISECONDS); if (!terminated) { throw new TimeoutException( - "The process " + id + " didn't exit within the defined timeout of " + timeout + "ms"); + "The process " + id + " didn't exit within the defined timeout of " + timeout + "ms"); } return process.exitValue(); } @@ -380,13 +380,13 @@ private void stopProcess(Process process) { recursiveStopProcess(process.toHandle()); } - private void recursiveStopProcess(ProcessHandle process) { + private void recursiveStopProcess(ProcessHandle process) { // kill all the children, depth first process.children().forEach(this::recursiveStopProcess); //Stop process and wait for completion process.destroy(); try { - int counter=0; + int counter = 0; while (process.isAlive() && counter < 100) { counter++; Thread.sleep(100); @@ -411,4 +411,4 @@ private void removeTempLogDirectory() { Thread.currentThread().interrupt(); } } -} \ No newline at end of file +} diff --git a/exense-basic-commons/src/test/java/ch/exense/commons/app/ArgumentParserTest.java b/exense-basic-commons/src/test/java/ch/exense/commons/app/ArgumentParserTest.java index f5fdd46..166ef3f 100644 --- a/exense-basic-commons/src/test/java/ch/exense/commons/app/ArgumentParserTest.java +++ b/exense-basic-commons/src/test/java/ch/exense/commons/app/ArgumentParserTest.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -21,13 +21,13 @@ public class ArgumentParserTest { - @Test - public void test() { - ArgumentParser argumentParser = new ArgumentParser(new String[] {"-param1=value1","-param2=value2"}); - Assert.assertTrue(argumentParser.hasOption("param1")); - Assert.assertTrue(argumentParser.hasOption("param2")); - String param1 = argumentParser.getOption("param1"); - Assert.assertEquals("value1", param1); - } + @Test + public void test() { + ArgumentParser argumentParser = new ArgumentParser(new String[]{"-param1=value1", "-param2=value2"}); + Assert.assertTrue(argumentParser.hasOption("param1")); + Assert.assertTrue(argumentParser.hasOption("param2")); + String param1 = argumentParser.getOption("param1"); + Assert.assertEquals("value1", param1); + } } diff --git a/exense-basic-commons/src/test/java/ch/exense/commons/app/ConfigurationTest.java b/exense-basic-commons/src/test/java/ch/exense/commons/app/ConfigurationTest.java index cb7a926..4178483 100644 --- a/exense-basic-commons/src/test/java/ch/exense/commons/app/ConfigurationTest.java +++ b/exense-basic-commons/src/test/java/ch/exense/commons/app/ConfigurationTest.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -30,65 +30,65 @@ public class ConfigurationTest { - @Test - public void test() throws IOException { - try(Configuration configuration = new Configuration(FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "test.properties"))) { - Assert.assertEquals("myProp1", configuration.getProperty("my.prop1")); - Assert.assertEquals((int)1000, (int)configuration.getPropertyAsInteger("my.prop2")); - Assert.assertEquals(false, configuration.getPropertyAsBoolean("my.prop3")); - Assert.assertEquals(100000000000000L, (long)configuration.getPropertyAsLong("my.prop5")); - Assert.assertEquals(new File("."), configuration.getPropertyAsFile("my.prop6")); - Assert.assertEquals(new File("."), configuration.getPropertyAsDirectory("my.prop6")); - - Assert.assertEquals(true, configuration.hasProperty("my.prop5")); - - configuration.putProperty("my.prop1", "changedProp"); - Assert.assertEquals("changedProp", configuration.getProperty("my.prop1")); - - configuration.putProperty("my.prop2", "1001"); - Assert.assertEquals((int)1001, (int)configuration.getPropertyAsInteger("my.prop2")); - - Assert.assertEquals("default", configuration.getProperty("notExistingProp","default")); - Assert.assertEquals((int)1005, (int)configuration.getPropertyAsInteger("notExistingProp",1005)); - Assert.assertEquals((long)1000000l, (long)configuration.getPropertyAsLong("notExistingProp",1000000l)); - Assert.assertEquals(false, configuration.getPropertyAsBoolean("notExistingProp",false)); - File defaultValue = new File("notExisting"); - Assert.assertEquals(defaultValue, configuration.getPropertyAsFile("notExistingProp",defaultValue)); - File tempFolder = FileHelper.createTempFolder(); - File myFolder = new File(tempFolder+"/myFolder"); - Assert.assertEquals(myFolder, configuration.getPropertyAsDirectory("notExistingProp",myFolder)); - Assert.assertTrue(myFolder.exists()); - FileHelper.deleteFolder(tempFolder); - } - } - - @Test - public void testScan() throws IOException, InterruptedException { - File propertyFile = FileHelper.createTempFile(); - FileHelper.copy(this.getClass().getClassLoader().getResourceAsStream("testScan.properties"), new FileOutputStream(propertyFile)); - try(Configuration configuration = new Configuration(propertyFile)) { - Assert.assertEquals("myProp1", configuration.getProperty("my.prop1")); - Assert.assertEquals((int)1000, (int)configuration.getPropertyAsInteger("my.prop2")); - Assert.assertEquals(false, configuration.getPropertyAsBoolean("my.prop3")); - Assert.assertEquals(100000000000000l, (long)configuration.getPropertyAsLong("my.prop5")); - - Thread.sleep(2000); - - Files.write(propertyFile.toPath(), new String("\nmyNewProp=myNewPropsValue").getBytes(), StandardOpenOption.APPEND, StandardOpenOption.SYNC); - - Thread.sleep(2000); - - Assert.assertEquals("myNewPropsValue", configuration.getProperty("myNewProp")); - - Assert.assertEquals(propertyFile, configuration.getPropertyFile()); - } - } - - @Test - public void testPlaceholder() throws IOException { - ArgumentParser argumentParser = new ArgumentParser(new String[] {"-myPlaceholder=myPlaceholdersValue"}); - try(Configuration configuration = new Configuration(FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "testPlaceholders.properties"), argumentParser.getOptions())) { - Assert.assertEquals("myPlaceholdersValue", configuration.getProperty("my.prop4")); - } - } + @Test + public void test() throws IOException { + try (Configuration configuration = new Configuration(FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "test.properties"))) { + Assert.assertEquals("myProp1", configuration.getProperty("my.prop1")); + Assert.assertEquals((int) 1000, (int) configuration.getPropertyAsInteger("my.prop2")); + Assert.assertEquals(false, configuration.getPropertyAsBoolean("my.prop3")); + Assert.assertEquals(100000000000000L, (long) configuration.getPropertyAsLong("my.prop5")); + Assert.assertEquals(new File("."), configuration.getPropertyAsFile("my.prop6")); + Assert.assertEquals(new File("."), configuration.getPropertyAsDirectory("my.prop6")); + + Assert.assertEquals(true, configuration.hasProperty("my.prop5")); + + configuration.putProperty("my.prop1", "changedProp"); + Assert.assertEquals("changedProp", configuration.getProperty("my.prop1")); + + configuration.putProperty("my.prop2", "1001"); + Assert.assertEquals((int) 1001, (int) configuration.getPropertyAsInteger("my.prop2")); + + Assert.assertEquals("default", configuration.getProperty("notExistingProp", "default")); + Assert.assertEquals((int) 1005, (int) configuration.getPropertyAsInteger("notExistingProp", 1005)); + Assert.assertEquals((long) 1000000l, (long) configuration.getPropertyAsLong("notExistingProp", 1000000l)); + Assert.assertEquals(false, configuration.getPropertyAsBoolean("notExistingProp", false)); + File defaultValue = new File("notExisting"); + Assert.assertEquals(defaultValue, configuration.getPropertyAsFile("notExistingProp", defaultValue)); + File tempFolder = FileHelper.createTempFolder(); + File myFolder = new File(tempFolder + "/myFolder"); + Assert.assertEquals(myFolder, configuration.getPropertyAsDirectory("notExistingProp", myFolder)); + Assert.assertTrue(myFolder.exists()); + FileHelper.deleteFolder(tempFolder); + } + } + + @Test + public void testScan() throws IOException, InterruptedException { + File propertyFile = FileHelper.createTempFile(); + FileHelper.copy(this.getClass().getClassLoader().getResourceAsStream("testScan.properties"), new FileOutputStream(propertyFile)); + try (Configuration configuration = new Configuration(propertyFile)) { + Assert.assertEquals("myProp1", configuration.getProperty("my.prop1")); + Assert.assertEquals((int) 1000, (int) configuration.getPropertyAsInteger("my.prop2")); + Assert.assertEquals(false, configuration.getPropertyAsBoolean("my.prop3")); + Assert.assertEquals(100000000000000l, (long) configuration.getPropertyAsLong("my.prop5")); + + Thread.sleep(2000); + + Files.write(propertyFile.toPath(), new String("\nmyNewProp=myNewPropsValue").getBytes(), StandardOpenOption.APPEND, StandardOpenOption.SYNC); + + Thread.sleep(2000); + + Assert.assertEquals("myNewPropsValue", configuration.getProperty("myNewProp")); + + Assert.assertEquals(propertyFile, configuration.getPropertyFile()); + } + } + + @Test + public void testPlaceholder() throws IOException { + ArgumentParser argumentParser = new ArgumentParser(new String[]{"-myPlaceholder=myPlaceholdersValue"}); + try (Configuration configuration = new Configuration(FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "testPlaceholders.properties"), argumentParser.getOptions())) { + Assert.assertEquals("myPlaceholdersValue", configuration.getProperty("my.prop4")); + } + } } diff --git a/exense-basic-commons/src/test/java/ch/exense/commons/io/FileHelperTest.java b/exense-basic-commons/src/test/java/ch/exense/commons/io/FileHelperTest.java index da56537..247bad1 100644 --- a/exense-basic-commons/src/test/java/ch/exense/commons/io/FileHelperTest.java +++ b/exense-basic-commons/src/test/java/ch/exense/commons/io/FileHelperTest.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -27,95 +27,95 @@ public class FileHelperTest { - @Test - public void test() throws Exception { - File tempFile = FileHelper.createTempFile(); - - Path tempDirectory = Files.createTempDirectory(null); - Path file1 = Paths.get(tempDirectory.toAbsolutePath()+"/file1"); - Files.write(file1, "TEST".getBytes()); - - Path subfolder1 = Paths.get(tempDirectory.toAbsolutePath()+"/subfolder1"); - subfolder1.toFile().mkdirs(); - - Path file2 = Paths.get(subfolder1.toAbsolutePath()+"/file2"); - Files.write(file2, "TEST".getBytes()); - - FileHelper.zip(tempDirectory.toFile(), tempFile); - - // classloader should see all files and folders as zip entires - try (URLClassLoader classLoader = new URLClassLoader(new URL[]{tempFile.toURI().toURL()}, null)) { - URL url = classLoader.getResource("file1"); - Assert.assertNotNull(url); - try (InputStream fileUrlStream = url.openStream()) { - byte[] bytes = fileUrlStream.readAllBytes(); - Assert.assertTrue(bytes.length > 0); - } - - url = classLoader.getResource("subfolder1/"); - Assert.assertNotNull(url); - - url = classLoader.getResource("subfolder1/file2"); - Assert.assertNotNull(url); - try (InputStream fileUrlStream = url.openStream()) { - byte[] bytes = fileUrlStream.readAllBytes(); - Assert.assertTrue(bytes.length > 0); - } - } - - byte[] bytes = FileHelper.zip(tempDirectory.toFile()); - - Path targetDirectory = Files.createTempDirectory(null); - long t1 = System.currentTimeMillis(); - Thread.sleep(10); // don't ask, otherwise the first Assert occasionally fails on some systems, probably because of FS caching/optimizations - FileHelper.unzip(tempFile, targetDirectory.toFile()); - - Path targetDirectory2 = Files.createTempDirectory(null); - FileHelper.unzip(bytes, targetDirectory2.toFile()); - - long lastModificationDate = FileHelper.getLastModificationDateRecursive(targetDirectory.toFile()); - - Assert.assertTrue("lastModificationDate >= t1 failed, lastModificationDate="+lastModificationDate+"; t1="+t1, lastModificationDate>=t1); - - String contentFile1 = new String(Files.readAllBytes(Paths.get(targetDirectory.toAbsolutePath().toString(), "file1"))); - Assert.assertEquals("TEST", contentFile1); - - String contentFile2 = new String(Files.readAllBytes(Paths.get(targetDirectory.toAbsolutePath().toString(), "subfolder1", "file2"))); - Assert.assertEquals("TEST", contentFile2); - - - FileHelper.deleteFolder(targetDirectory.toFile()); - Assert.assertFalse(targetDirectory.toFile().exists()); - - FileHelper.deleteFolderOnExit(targetDirectory2.toFile()); - Assert.assertTrue(targetDirectory2.toFile().exists()); - } - - @Test - public void test2() throws IOException { - String readResource = FileHelper.readResource(getClass(), "testFile.txt"); - Assert.assertEquals("TEST FILE", readResource); - - byte[] readResourceAsByteArray = FileHelper.readResourceAsByteArray(getClass(), "testFile.txt"); - Assert.assertEquals("TEST FILE", new String(readResourceAsByteArray)); - - String readClassLoaderResource = FileHelper.readClassLoaderResource(getClass().getClassLoader(), "testClassloaderResource.txt"); - Assert.assertEquals("TEST FILE", readClassLoaderResource); - - byte[] readClassLoaderResourceAsByteArray = FileHelper.readClassLoaderResourceAsByteArray(getClass().getClassLoader(), "testClassloaderResource.txt"); - Assert.assertEquals("TEST FILE", new String(readClassLoaderResourceAsByteArray)); - - File classLoaderResourceAsFile = FileHelper.getClassLoaderResourceAsFile(getClass().getClassLoader(), "testClassloaderResource.txt"); - String content = new String(Files.readAllBytes(classLoaderResourceAsFile.toPath())); - Assert.assertEquals("TEST FILE", content); - - File tempFile = FileHelper.createTempFile(); - FileHelper.copy(new FileInputStream(classLoaderResourceAsFile), new FileOutputStream(tempFile)); - String contentTempFile = new String(Files.readAllBytes(tempFile.toPath())); - Assert.assertEquals("TEST FILE", contentTempFile); - - File tempFile2 = FileHelper.extractResourceToTempFile(getClass(), "testFile.txt"); - String contentTempFile2 = new String(Files.readAllBytes(tempFile2.toPath())); - Assert.assertEquals("TEST FILE", contentTempFile2); - } + @Test + public void test() throws Exception { + File tempFile = FileHelper.createTempFile(); + + Path tempDirectory = Files.createTempDirectory(null); + Path file1 = Paths.get(tempDirectory.toAbsolutePath() + "/file1"); + Files.write(file1, "TEST".getBytes()); + + Path subfolder1 = Paths.get(tempDirectory.toAbsolutePath() + "/subfolder1"); + subfolder1.toFile().mkdirs(); + + Path file2 = Paths.get(subfolder1.toAbsolutePath() + "/file2"); + Files.write(file2, "TEST".getBytes()); + + FileHelper.zip(tempDirectory.toFile(), tempFile); + + // classloader should see all files and folders as zip entires + try (URLClassLoader classLoader = new URLClassLoader(new URL[]{tempFile.toURI().toURL()}, null)) { + URL url = classLoader.getResource("file1"); + Assert.assertNotNull(url); + try (InputStream fileUrlStream = url.openStream()) { + byte[] bytes = fileUrlStream.readAllBytes(); + Assert.assertTrue(bytes.length > 0); + } + + url = classLoader.getResource("subfolder1/"); + Assert.assertNotNull(url); + + url = classLoader.getResource("subfolder1/file2"); + Assert.assertNotNull(url); + try (InputStream fileUrlStream = url.openStream()) { + byte[] bytes = fileUrlStream.readAllBytes(); + Assert.assertTrue(bytes.length > 0); + } + } + + byte[] bytes = FileHelper.zip(tempDirectory.toFile()); + + Path targetDirectory = Files.createTempDirectory(null); + long t1 = System.currentTimeMillis(); + Thread.sleep(10); // don't ask, otherwise the first Assert occasionally fails on some systems, probably because of FS caching/optimizations + FileHelper.unzip(tempFile, targetDirectory.toFile()); + + Path targetDirectory2 = Files.createTempDirectory(null); + FileHelper.unzip(bytes, targetDirectory2.toFile()); + + long lastModificationDate = FileHelper.getLastModificationDateRecursive(targetDirectory.toFile()); + + Assert.assertTrue("lastModificationDate >= t1 failed, lastModificationDate=" + lastModificationDate + "; t1=" + t1, lastModificationDate >= t1); + + String contentFile1 = new String(Files.readAllBytes(Paths.get(targetDirectory.toAbsolutePath().toString(), "file1"))); + Assert.assertEquals("TEST", contentFile1); + + String contentFile2 = new String(Files.readAllBytes(Paths.get(targetDirectory.toAbsolutePath().toString(), "subfolder1", "file2"))); + Assert.assertEquals("TEST", contentFile2); + + + FileHelper.deleteFolder(targetDirectory.toFile()); + Assert.assertFalse(targetDirectory.toFile().exists()); + + FileHelper.deleteFolderOnExit(targetDirectory2.toFile()); + Assert.assertTrue(targetDirectory2.toFile().exists()); + } + + @Test + public void test2() throws IOException { + String readResource = FileHelper.readResource(getClass(), "testFile.txt"); + Assert.assertEquals("TEST FILE", readResource); + + byte[] readResourceAsByteArray = FileHelper.readResourceAsByteArray(getClass(), "testFile.txt"); + Assert.assertEquals("TEST FILE", new String(readResourceAsByteArray)); + + String readClassLoaderResource = FileHelper.readClassLoaderResource(getClass().getClassLoader(), "testClassloaderResource.txt"); + Assert.assertEquals("TEST FILE", readClassLoaderResource); + + byte[] readClassLoaderResourceAsByteArray = FileHelper.readClassLoaderResourceAsByteArray(getClass().getClassLoader(), "testClassloaderResource.txt"); + Assert.assertEquals("TEST FILE", new String(readClassLoaderResourceAsByteArray)); + + File classLoaderResourceAsFile = FileHelper.getClassLoaderResourceAsFile(getClass().getClassLoader(), "testClassloaderResource.txt"); + String content = new String(Files.readAllBytes(classLoaderResourceAsFile.toPath())); + Assert.assertEquals("TEST FILE", content); + + File tempFile = FileHelper.createTempFile(); + FileHelper.copy(new FileInputStream(classLoaderResourceAsFile), new FileOutputStream(tempFile)); + String contentTempFile = new String(Files.readAllBytes(tempFile.toPath())); + Assert.assertEquals("TEST FILE", contentTempFile); + + File tempFile2 = FileHelper.extractResourceToTempFile(getClass(), "testFile.txt"); + String contentTempFile2 = new String(Files.readAllBytes(tempFile2.toPath())); + Assert.assertEquals("TEST FILE", contentTempFile2); + } } diff --git a/exense-basic-commons/src/test/java/ch/exense/commons/io/FileWatchServiceTest.java b/exense-basic-commons/src/test/java/ch/exense/commons/io/FileWatchServiceTest.java index 31bd468..0a95f8c 100644 --- a/exense-basic-commons/src/test/java/ch/exense/commons/io/FileWatchServiceTest.java +++ b/exense-basic-commons/src/test/java/ch/exense/commons/io/FileWatchServiceTest.java @@ -32,103 +32,105 @@ public class FileWatchServiceTest { - @Test - public void testBasic() throws InterruptedException, IOException { - FileWatchService fileWatchService = new FileWatchService(); - File file = FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(),"FileWatchServiceTest.test"); - long lastModified = 0; - file.setLastModified(lastModified); - - Object lock = new Object(); - - final AtomicInteger updatedCount = new AtomicInteger(0); - fileWatchService.setInterval(10); - fileWatchService.register(file, new Runnable() { - @Override - public void run() { - synchronized (lock) { - updatedCount.incrementAndGet(); - lock.notify(); - } - } - }); - - - touchAndWait(file, lock, updatedCount, 1, lastModified+10000); - touchAndWait(file, lock, updatedCount, 2, lastModified+20000); - - fileWatchService.unregister(file); - touchAndWait(file, lock, updatedCount, 2, lastModified+30000); - - fileWatchService.close(); - } - - @Test - public void testConcurrentModification() throws Exception { - - // hook into logging so we can see if an exception was logged - Logger logger = (Logger) LoggerFactory.getLogger(FileWatchService.class); - - AtomicInteger exceptions = new AtomicInteger(); - - Appender appender = new AppenderBase() { - @Override - protected void append(ILoggingEvent event) { - if (event.getLevel().equals(Level.ERROR)) { - if (event.getThrowableProxy() != null && event.getThrowableProxy().getClassName().equals(ConcurrentModificationException.class.getName())) { - exceptions.incrementAndGet(); - } - } - } - }; - - appender.start(); - logger.addAppender(appender); - - FileWatchService fileWatchService = new FileWatchService(); - File file = FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "FileWatchServiceTest.test"); - File file2 = FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "FileWatchServiceTest2.test"); - File file3 = FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "FileWatchServiceTest3.test"); - - long lastModified = 0; - file.setLastModified(lastModified); - - fileWatchService.setInterval(500); - - Object lock = new Object(); - - - final AtomicInteger updatedCount = new AtomicInteger(0); - - AtomicReference runref = new AtomicReference<>(); - Runnable runnable = () -> { - fileWatchService.unregister(file); - fileWatchService.register(file, runref.get()); - synchronized (lock) { - updatedCount.incrementAndGet(); - lock.notify(); - } - }; - runref.set(runnable); - - fileWatchService.register(file2, () -> {}); - fileWatchService.register(file, runref.get()); - fileWatchService.register(file3, () -> {}); - - for (int i=1; i <= 5; ++i) { - touchAndWait(file, lock, updatedCount, i, i); - } - fileWatchService.close(); - - Assert.assertEquals(0, exceptions.get()); - } - - private void touchAndWait(File file, Object lock, final AtomicInteger updatedCount, int expected, long lastModified) throws InterruptedException { - synchronized (lock) { - file.setLastModified(lastModified); - lock.wait(100); - } - Thread.sleep(1000); - Assert.assertEquals(expected, updatedCount.get()); - } + @Test + public void testBasic() throws InterruptedException, IOException { + FileWatchService fileWatchService = new FileWatchService(); + File file = FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "FileWatchServiceTest.test"); + long lastModified = 0; + file.setLastModified(lastModified); + + Object lock = new Object(); + + final AtomicInteger updatedCount = new AtomicInteger(0); + fileWatchService.setInterval(10); + fileWatchService.register(file, new Runnable() { + @Override + public void run() { + synchronized (lock) { + updatedCount.incrementAndGet(); + lock.notify(); + } + } + }); + + + touchAndWait(file, lock, updatedCount, 1, lastModified + 10000); + touchAndWait(file, lock, updatedCount, 2, lastModified + 20000); + + fileWatchService.unregister(file); + touchAndWait(file, lock, updatedCount, 2, lastModified + 30000); + + fileWatchService.close(); + } + + @Test + public void testConcurrentModification() throws Exception { + + // hook into logging so we can see if an exception was logged + Logger logger = (Logger) LoggerFactory.getLogger(FileWatchService.class); + + AtomicInteger exceptions = new AtomicInteger(); + + Appender appender = new AppenderBase() { + @Override + protected void append(ILoggingEvent event) { + if (event.getLevel().equals(Level.ERROR)) { + if (event.getThrowableProxy() != null && event.getThrowableProxy().getClassName().equals(ConcurrentModificationException.class.getName())) { + exceptions.incrementAndGet(); + } + } + } + }; + + appender.start(); + logger.addAppender(appender); + + FileWatchService fileWatchService = new FileWatchService(); + File file = FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "FileWatchServiceTest.test"); + File file2 = FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "FileWatchServiceTest2.test"); + File file3 = FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "FileWatchServiceTest3.test"); + + long lastModified = 0; + file.setLastModified(lastModified); + + fileWatchService.setInterval(500); + + Object lock = new Object(); + + + final AtomicInteger updatedCount = new AtomicInteger(0); + + AtomicReference runref = new AtomicReference<>(); + Runnable runnable = () -> { + fileWatchService.unregister(file); + fileWatchService.register(file, runref.get()); + synchronized (lock) { + updatedCount.incrementAndGet(); + lock.notify(); + } + }; + runref.set(runnable); + + fileWatchService.register(file2, () -> { + }); + fileWatchService.register(file, runref.get()); + fileWatchService.register(file3, () -> { + }); + + for (int i = 1; i <= 5; ++i) { + touchAndWait(file, lock, updatedCount, i, i); + } + fileWatchService.close(); + + Assert.assertEquals(0, exceptions.get()); + } + + private void touchAndWait(File file, Object lock, final AtomicInteger updatedCount, int expected, long lastModified) throws InterruptedException { + synchronized (lock) { + file.setLastModified(lastModified); + lock.wait(100); + } + Thread.sleep(1000); + Assert.assertEquals(expected, updatedCount.get()); + } } diff --git a/exense-basic-commons/src/test/java/ch/exense/commons/io/PollerTest.java b/exense-basic-commons/src/test/java/ch/exense/commons/io/PollerTest.java index b40b139..66f95ea 100644 --- a/exense-basic-commons/src/test/java/ch/exense/commons/io/PollerTest.java +++ b/exense-basic-commons/src/test/java/ch/exense/commons/io/PollerTest.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -22,10 +22,10 @@ public class PollerTest { - @Test - public void test() throws TimeoutException, InterruptedException { - AtomicInteger count = new AtomicInteger(); - Poller.waitFor(()->count.incrementAndGet()==5, 1000); - } + @Test + public void test() throws TimeoutException, InterruptedException { + AtomicInteger count = new AtomicInteger(); + Poller.waitFor(() -> count.incrementAndGet() == 5, 1000); + } } diff --git a/exense-basic-commons/src/test/java/ch/exense/commons/processes/ExternalJVMLauncherTest.java b/exense-basic-commons/src/test/java/ch/exense/commons/processes/ExternalJVMLauncherTest.java index b4205b5..838fd38 100644 --- a/exense-basic-commons/src/test/java/ch/exense/commons/processes/ExternalJVMLauncherTest.java +++ b/exense-basic-commons/src/test/java/ch/exense/commons/processes/ExternalJVMLauncherTest.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -29,47 +29,47 @@ public class ExternalJVMLauncherTest { - private static final String OUTPUT = "OK"; + private static final String OUTPUT = "OK"; - public static void main(String[] args) { - System.out.print(OUTPUT); - } + public static void main(String[] args) { + System.out.print(OUTPUT); + } - @Test - public void test() throws ManagedProcessException, IOException, TimeoutException, InterruptedException { - ExternalJVMLauncher externalJVMLauncher = new ExternalJVMLauncher(javaExe(), new File("log")); - try (ManagedProcess externalVM = externalJVMLauncher.launchExternalJVM("MyExternalProcess", ExternalJVMLauncherTest.class, - new ArrayList<>(), new ArrayList<>())) { - externalVM.waitFor(1000); - String output = new String(Files.readAllBytes(externalVM.getProcessOutputLog().toPath())); - assertEquals(OUTPUT, output); - } - } + @Test + public void test() throws ManagedProcessException, IOException, TimeoutException, InterruptedException { + ExternalJVMLauncher externalJVMLauncher = new ExternalJVMLauncher(javaExe(), new File("log")); + try (ManagedProcess externalVM = externalJVMLauncher.launchExternalJVM("MyExternalProcess", ExternalJVMLauncherTest.class, + new ArrayList<>(), new ArrayList<>())) { + externalVM.waitFor(1000); + String output = new String(Files.readAllBytes(externalVM.getProcessOutputLog().toPath())); + assertEquals(OUTPUT, output); + } + } - private static String javaExe() { - final String JAVA_HOME = System.getProperty("java.home"); - final File BIN = new File(JAVA_HOME, "bin"); - File exe = new File(BIN, "java"); + private static String javaExe() { + final String JAVA_HOME = System.getProperty("java.home"); + final File BIN = new File(JAVA_HOME, "bin"); + File exe = new File(BIN, "java"); - if (!exe.exists()) { - // We might be on Windows, which needs an exe extension - exe = new File(BIN, "java.exe"); - } + if (!exe.exists()) { + // We might be on Windows, which needs an exe extension + exe = new File(BIN, "java.exe"); + } - if (exe.exists()) { - return exe.getAbsolutePath(); - } + if (exe.exists()) { + return exe.getAbsolutePath(); + } - try { - // Just try invoking java from the system path; this of course - // assumes "java[.exe]" is /actually/ Java - final String NAKED_JAVA = "java"; - new ProcessBuilder(NAKED_JAVA).start(); + try { + // Just try invoking java from the system path; this of course + // assumes "java[.exe]" is /actually/ Java + final String NAKED_JAVA = "java"; + new ProcessBuilder(NAKED_JAVA).start(); - return NAKED_JAVA; - } catch (IOException e) { - return null; - } - } + return NAKED_JAVA; + } catch (IOException e) { + return null; + } + } } diff --git a/exense-basic-commons/src/test/java/ch/exense/commons/processes/ManagedProcessTest.java b/exense-basic-commons/src/test/java/ch/exense/commons/processes/ManagedProcessTest.java index 11ceb1e..608a022 100644 --- a/exense-basic-commons/src/test/java/ch/exense/commons/processes/ManagedProcessTest.java +++ b/exense-basic-commons/src/test/java/ch/exense/commons/processes/ManagedProcessTest.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2021 exense GmbH - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -25,206 +25,206 @@ import static org.junit.Assert.*; -public class ManagedProcessTest { - - @Test - public void test() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { - try(ManagedProcess managedProcess = new ManagedProcess("java -version")) { - runAndTestProcess(managedProcess); - } - } - - @Test - public void test2() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { - try(ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", "java -version")) { - runAndTestProcess(managedProcess); - assertTrue(managedProcess.getId().startsWith("MyJavaProcess_")); - } - } - - @Test - public void test3() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { - try(ManagedProcess managedProcess = new ManagedProcess(Arrays.asList("java", "-version"))) { - runAndTestProcess(managedProcess); - } - } - - @Test - public void test4() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { - try(ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "-version"))) { - runAndTestProcess(managedProcess); - assertTrue(managedProcess.getId().startsWith("MyJavaProcess_")); - } - } - - @Test - public void test5() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { - File tempFolder = FileHelper.createTempFolder(); - try(ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "-version"), tempFolder, true)) { - runAndTestProcess(managedProcess); - assertTrue(managedProcess.getId().startsWith("MyJavaProcess_")); - assertEquals(managedProcess.getId(), tempFolder.listFiles()[0].getName()); - } - // Assert that the process log have been deleted - assertEquals(0, tempFolder.listFiles().length); - } - - @Test - public void testClose() throws ManagedProcessException, InterruptedException, TimeoutException, IOException { - File tempFolder = FileHelper.createTempFolder(); - ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "-version"), tempFolder, true); - runAndTestProcess(managedProcess); - try { - managedProcess.close(); - } catch (IOException e) { - // Ensure that the method close throws an IOException to not break backward compatibility - throw new RuntimeException(e); - } - - // Assert that the process log have been deleted - assertEquals(0, tempFolder.listFiles().length); - } - - @Test - public void testExecutionDirectory() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { - File tempExecutionFolder = FileHelper.createTempFolder(); - - // Create a simple java program - FileWriter fileWriter = new FileWriter(tempExecutionFolder.toPath().resolve("test.java").toFile()); - fileWriter.write("public class HelloWorld {\n" + - "\n" + - " public static void main(String[] args) {\n" + - " System.out.println(\"Hello World!\");\n" + - " System.err.println(\"Error World\");\n" + - " }\n" + - "\n" + - "}"); - fileWriter.close(); - - File tempLogFolder = FileHelper.createTempFolder(); - try(ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "test.java"), tempExecutionFolder, tempLogFolder, true)) { - managedProcess.startAndWaitFor(5000); - assertTrue(managedProcess.getProcessOutputLogAsString().startsWith("Hello World!")); - assertTrue(managedProcess.getProcessErrorLogAsString().startsWith("Error World")); - String processLog = managedProcess.getProcessLog(); - assertTrue(processLog.contains("Hello World!")); - assertTrue(processLog.contains("Error World")); - - // Assert that the process logs have been created within the log directory - assertEquals(2, tempLogFolder.toPath().resolve(managedProcess.getId()).toFile().listFiles().length); - // Assert that the process returns the correct execution directory - assertEquals(tempExecutionFolder, managedProcess.getExecutionDirectory()); - } - // Assert that the process log have been deleted - assertEquals(0, tempLogFolder.listFiles().length); - // Assert that the execution directory still exist - assertEquals(1, tempExecutionFolder.listFiles().length); - } - - @Test - public void testExecutionDirectory2() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { - File tempExecutionFolder = FileHelper.createTempFolder(); - - // Create a simple java program - FileWriter fileWriter = new FileWriter(tempExecutionFolder.toPath().resolve("test.java").toFile()); - fileWriter.write("public class HelloWorld {\n" + - "\n" + - " public static void main(String[] args) {\n" + - " System.out.println(\"Hello World!\");\n" + - " System.err.println(\"Error World\");\n" + - " }\n" + - "\n" + - "}"); - fileWriter.close(); - - File logFolder = null; - try(ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "test.java"), tempExecutionFolder)) { - managedProcess.startAndWaitFor(5000); - assertTrue(managedProcess.getProcessOutputLogAsString().startsWith("Hello World!")); - assertTrue(managedProcess.getProcessErrorLogAsString().startsWith("Error World")); - String processLog = managedProcess.getProcessLog(); - assertTrue(processLog.contains("Hello World!")); - assertTrue(processLog.contains("Error World")); - - logFolder = tempExecutionFolder.toPath().resolve(managedProcess.getId()).toFile(); - // Assert that the process logs have been created within the log directory - assertEquals(2, logFolder.listFiles().length); - // Assert that the process returns the correct execution directory - assertEquals(tempExecutionFolder, managedProcess.getExecutionDirectory()); - } - // Assert that the process log have been deleted - assertFalse(logFolder.exists()); - // Assert that the execution directory still exist - assertTrue(tempExecutionFolder.exists()); - assertEquals(1, tempExecutionFolder.listFiles().length); - } - - @Test - public void testInputOutputStream() throws Exception { - File tempExecutionFolder = FileHelper.createTempFolder(); - - // Create a simple java program that prints the content of the input stream - FileWriter fileWriter = new FileWriter(tempExecutionFolder.toPath().resolve("test.java").toFile()); - fileWriter.write("public class HelloWorld {\n" + - " public static void main(String[] args) throws java.io.IOException {\n" + - " java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));\n" + - " String name = reader.readLine();\n" + - " System.out.println(name);" + - " }\n" + - "}"); - fileWriter.close(); - - File tempLogFolder = FileHelper.createTempFolder(); - try(ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "test.java"), tempExecutionFolder, tempLogFolder, false)) { - managedProcess.start(); - OutputStreamWriter writer = new OutputStreamWriter(managedProcess.getProcessOutputStream()); - writer.write("hello\n"); - writer.close(); - managedProcess.waitFor(2000); - - InputStream processInputStream = managedProcess.getProcessInputStream(); - BufferedReader inputStreamReader = new BufferedReader(new InputStreamReader(processInputStream)); - String line = inputStreamReader.readLine(); - assertTrue(line.startsWith("hello")); - } - } - - @Test - public void testError() throws IOException, TimeoutException, InterruptedException { - Exception actual = null; - try(ManagedProcess managedProcess = new ManagedProcess("invalid cmd")) { - runAndTestProcess(managedProcess); - } catch (ManagedProcessException e) { - actual = e; - } - assertNotNull(actual); - assertTrue(actual.getMessage().startsWith("Unable to start the process ")); - } - - @Test - public void testMultipleStarts() throws IOException, TimeoutException, InterruptedException { - Exception actual = null; - String id = null; - try(ManagedProcess managedProcess = new ManagedProcess("java -version")) { - id = managedProcess.getId(); - runAndTestProcess(managedProcess); - runAndTestProcess(managedProcess); - } catch (ManagedProcessException e) { - actual = e; - } - assertNotNull(actual); - assertEquals("Unable to start the process "+id+ " twice. The process has already been started.", actual.getMessage()); - } - - private void runAndTestProcess(ManagedProcess managedProcess) - throws ManagedProcessException, TimeoutException, InterruptedException { - managedProcess.start(); - int exitValue = managedProcess.waitFor(1000); - String errOutput = managedProcess.getProcessErrorLogAsString(); - String stdOutput = managedProcess.getProcessOutputLogAsString(); - assertEquals(0, exitValue); - assertTrue(errOutput.contains("version")); - assertTrue(stdOutput.isEmpty()); - } +public class ManagedProcessTest { + + @Test + public void test() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { + try (ManagedProcess managedProcess = new ManagedProcess("java -version")) { + runAndTestProcess(managedProcess); + } + } + + @Test + public void test2() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { + try (ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", "java -version")) { + runAndTestProcess(managedProcess); + assertTrue(managedProcess.getId().startsWith("MyJavaProcess_")); + } + } + + @Test + public void test3() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { + try (ManagedProcess managedProcess = new ManagedProcess(Arrays.asList("java", "-version"))) { + runAndTestProcess(managedProcess); + } + } + + @Test + public void test4() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { + try (ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "-version"))) { + runAndTestProcess(managedProcess); + assertTrue(managedProcess.getId().startsWith("MyJavaProcess_")); + } + } + + @Test + public void test5() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { + File tempFolder = FileHelper.createTempFolder(); + try (ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "-version"), tempFolder, true)) { + runAndTestProcess(managedProcess); + assertTrue(managedProcess.getId().startsWith("MyJavaProcess_")); + assertEquals(managedProcess.getId(), tempFolder.listFiles()[0].getName()); + } + // Assert that the process log have been deleted + assertEquals(0, tempFolder.listFiles().length); + } + + @Test + public void testClose() throws ManagedProcessException, InterruptedException, TimeoutException, IOException { + File tempFolder = FileHelper.createTempFolder(); + ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "-version"), tempFolder, true); + runAndTestProcess(managedProcess); + try { + managedProcess.close(); + } catch (IOException e) { + // Ensure that the method close throws an IOException to not break backward compatibility + throw new RuntimeException(e); + } + + // Assert that the process log have been deleted + assertEquals(0, tempFolder.listFiles().length); + } + + @Test + public void testExecutionDirectory() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { + File tempExecutionFolder = FileHelper.createTempFolder(); + + // Create a simple java program + FileWriter fileWriter = new FileWriter(tempExecutionFolder.toPath().resolve("test.java").toFile()); + fileWriter.write("public class HelloWorld {\n" + + "\n" + + " public static void main(String[] args) {\n" + + " System.out.println(\"Hello World!\");\n" + + " System.err.println(\"Error World\");\n" + + " }\n" + + "\n" + + "}"); + fileWriter.close(); + + File tempLogFolder = FileHelper.createTempFolder(); + try (ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "test.java"), tempExecutionFolder, tempLogFolder, true)) { + managedProcess.startAndWaitFor(5000); + assertTrue(managedProcess.getProcessOutputLogAsString().startsWith("Hello World!")); + assertTrue(managedProcess.getProcessErrorLogAsString().startsWith("Error World")); + String processLog = managedProcess.getProcessLog(); + assertTrue(processLog.contains("Hello World!")); + assertTrue(processLog.contains("Error World")); + + // Assert that the process logs have been created within the log directory + assertEquals(2, tempLogFolder.toPath().resolve(managedProcess.getId()).toFile().listFiles().length); + // Assert that the process returns the correct execution directory + assertEquals(tempExecutionFolder, managedProcess.getExecutionDirectory()); + } + // Assert that the process log have been deleted + assertEquals(0, tempLogFolder.listFiles().length); + // Assert that the execution directory still exist + assertEquals(1, tempExecutionFolder.listFiles().length); + } + + @Test + public void testExecutionDirectory2() throws IOException, ManagedProcessException, TimeoutException, InterruptedException { + File tempExecutionFolder = FileHelper.createTempFolder(); + + // Create a simple java program + FileWriter fileWriter = new FileWriter(tempExecutionFolder.toPath().resolve("test.java").toFile()); + fileWriter.write("public class HelloWorld {\n" + + "\n" + + " public static void main(String[] args) {\n" + + " System.out.println(\"Hello World!\");\n" + + " System.err.println(\"Error World\");\n" + + " }\n" + + "\n" + + "}"); + fileWriter.close(); + + File logFolder = null; + try (ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "test.java"), tempExecutionFolder)) { + managedProcess.startAndWaitFor(5000); + assertTrue(managedProcess.getProcessOutputLogAsString().startsWith("Hello World!")); + assertTrue(managedProcess.getProcessErrorLogAsString().startsWith("Error World")); + String processLog = managedProcess.getProcessLog(); + assertTrue(processLog.contains("Hello World!")); + assertTrue(processLog.contains("Error World")); + + logFolder = tempExecutionFolder.toPath().resolve(managedProcess.getId()).toFile(); + // Assert that the process logs have been created within the log directory + assertEquals(2, logFolder.listFiles().length); + // Assert that the process returns the correct execution directory + assertEquals(tempExecutionFolder, managedProcess.getExecutionDirectory()); + } + // Assert that the process log have been deleted + assertFalse(logFolder.exists()); + // Assert that the execution directory still exist + assertTrue(tempExecutionFolder.exists()); + assertEquals(1, tempExecutionFolder.listFiles().length); + } + + @Test + public void testInputOutputStream() throws Exception { + File tempExecutionFolder = FileHelper.createTempFolder(); + + // Create a simple java program that prints the content of the input stream + FileWriter fileWriter = new FileWriter(tempExecutionFolder.toPath().resolve("test.java").toFile()); + fileWriter.write("public class HelloWorld {\n" + + " public static void main(String[] args) throws java.io.IOException {\n" + + " java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));\n" + + " String name = reader.readLine();\n" + + " System.out.println(name);" + + " }\n" + + "}"); + fileWriter.close(); + + File tempLogFolder = FileHelper.createTempFolder(); + try (ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", Arrays.asList("java", "test.java"), tempExecutionFolder, tempLogFolder, false)) { + managedProcess.start(); + OutputStreamWriter writer = new OutputStreamWriter(managedProcess.getProcessOutputStream()); + writer.write("hello\n"); + writer.close(); + managedProcess.waitFor(2000); + + InputStream processInputStream = managedProcess.getProcessInputStream(); + BufferedReader inputStreamReader = new BufferedReader(new InputStreamReader(processInputStream)); + String line = inputStreamReader.readLine(); + assertTrue(line.startsWith("hello")); + } + } + + @Test + public void testError() throws IOException, TimeoutException, InterruptedException { + Exception actual = null; + try (ManagedProcess managedProcess = new ManagedProcess("invalid cmd")) { + runAndTestProcess(managedProcess); + } catch (ManagedProcessException e) { + actual = e; + } + assertNotNull(actual); + assertTrue(actual.getMessage().startsWith("Unable to start the process ")); + } + + @Test + public void testMultipleStarts() throws IOException, TimeoutException, InterruptedException { + Exception actual = null; + String id = null; + try (ManagedProcess managedProcess = new ManagedProcess("java -version")) { + id = managedProcess.getId(); + runAndTestProcess(managedProcess); + runAndTestProcess(managedProcess); + } catch (ManagedProcessException e) { + actual = e; + } + assertNotNull(actual); + assertEquals("Unable to start the process " + id + " twice. The process has already been started.", actual.getMessage()); + } + + private void runAndTestProcess(ManagedProcess managedProcess) + throws ManagedProcessException, TimeoutException, InterruptedException { + managedProcess.start(); + int exitValue = managedProcess.waitFor(1000); + String errOutput = managedProcess.getProcessErrorLogAsString(); + String stdOutput = managedProcess.getProcessOutputLogAsString(); + assertEquals(0, exitValue); + assertTrue(errOutput.contains("version")); + assertTrue(stdOutput.isEmpty()); + } } diff --git a/exense-basic-commons/src/test/java/ch/exense/commons/processes/RecursiveProcessTest.java b/exense-basic-commons/src/test/java/ch/exense/commons/processes/RecursiveProcessTest.java index 838ec98..b3f4a47 100644 --- a/exense-basic-commons/src/test/java/ch/exense/commons/processes/RecursiveProcessTest.java +++ b/exense-basic-commons/src/test/java/ch/exense/commons/processes/RecursiveProcessTest.java @@ -57,11 +57,11 @@ public static void main(String[] args) throws Exception { List argsForChild = new ArrayList<>(); argsForChild.add(String.valueOf(nbProcess)); argsForChild.add(String.valueOf(depth - 1)); - + for (int i = 0; i < nbProcess; i++) { ExternalJVMLauncher externalJVMLauncher = new ExternalJVMLauncher("java", Files.createTempDirectory("log_").toFile()); externalJVMLauncher.launchExternalJVM("MyExternalProcess", RecursiveProcessTest.class, new ArrayList<>(), - argsForChild); + argsForChild); } } // and wait @@ -99,7 +99,7 @@ private void startRandomProcesses() { for (int i = 0; i < NB_THREADS; i++) { threadPool.submit(() -> { while (true) { - try(ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", "java -version")) { + try (ManagedProcess managedProcess = new ManagedProcess("MyJavaProcess", "java -version")) { managedProcess.waitFor(10); } catch (InterruptedException | TimeoutException | ManagedProcessException | IOException e) { throw new RuntimeException(e); diff --git a/exense-basic-commons/src/test/resources/logback-test.xml b/exense-basic-commons/src/test/resources/logback-test.xml index 2489327..81c6069 100644 --- a/exense-basic-commons/src/test/resources/logback-test.xml +++ b/exense-basic-commons/src/test/resources/logback-test.xml @@ -1,11 +1,11 @@ - - - %date %level [%thread] %logger{10} [%file:%line] %msg%n - - + + + %date %level [%thread] %logger{10} [%file:%line] %msg%n + + - - - - \ No newline at end of file + + + + diff --git a/pom.xml b/pom.xml index 5e2b915..6afb02c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,354 +1,354 @@ - 4.0.0 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - ch.exense.commons - exense-commons - 0.0.0-SNAPSHOT - pom + ch.exense.commons + exense-commons + 0.0.0-SNAPSHOT + pom - - - - nexus-staging - https://nexus-enterprise-staging.exense.ch/repository/staging-maven/ - - + + + + nexus-staging + https://nexus-enterprise-staging.exense.ch/repository/staging-maven/ + + - - ${project.groupId}:${project.artifactId} - Exense Commons - https://github.com/exense/exense-commons + + ${project.groupId}:${project.artifactId} + Exense Commons + https://github.com/exense/exense-commons - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + - - - Exense GmbH - Exense GmbH - https://exense.ch - support@exense.ch - - + + + Exense GmbH + Exense GmbH + https://exense.ch + support@exense.ch + + - - scm:git:https://github.com/exense/exense-commons.git - scm:git:https://github.com/exense/exense-commons.git - https://github.com/exense/exense-commons - - + + scm:git:https://github.com/exense/exense-commons.git + scm:git:https://github.com/exense/exense-commons.git + https://github.com/exense/exense-commons + + - - UTF-8 - 11 - 11 - 11 + + UTF-8 + 11 + 11 + 11 - - 2025.6.25 + + 2025.6.25 - - 3.14.0 - 3.4.2 - 0.8.12 - 3.0.0-M1 - 2.19.1 - 3.3.2 - 3.0.1 - 1.6 - 10.0.3 - + + 3.14.0 + 3.4.2 + 0.8.12 + 3.0.0-M1 + 2.19.1 + 3.3.2 + 3.0.1 + 1.6 + 10.0.3 + - - - exense-basic-commons - + + + exense-basic-commons + - - - - - - ch.exense.dependencies - dependencies-junit - ${dependencies.version} - pom - import - - - ch.exense.dependencies - dependencies-logging - ${dependencies.version} - pom - import - + + + + + + ch.exense.dependencies + dependencies-junit + ${dependencies.version} + pom + import + + + ch.exense.dependencies + dependencies-logging + ${dependencies.version} + pom + import + - - - ch.exense.commons - exense-basic-commons - ${project.version} - + + + ch.exense.commons + exense-basic-commons + ${project.version} + - + - + - - - - junit - junit - test - - + + + + junit + junit + test + + - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${dep.mvn.compiler.version} - - - org.apache.maven.plugins - maven-jar-plugin - ${dep.mvn.jar.version} - - - org.apache.maven.plugins - maven-deploy-plugin - ${dep.mvn.deploy.version} - - - org.apache.maven.plugins - maven-source-plugin - ${dep.mvn.source.version} - - - org.apache.maven.plugins - maven-javadoc-plugin - ${dep.mvn.javadoc.version} - - - org.jacoco - jacoco-maven-plugin - ${dep.mvn.jacoco.version} - - - org.apache.maven.plugins - maven-surefire-plugin - ${dep.mvn.surefire.version} - - - org.apache.maven.surefire - surefire-junit47 - ${dep.mvn.surefire.version} - - - - - org.owasp - dependency-check-maven - ${dep.mvn.dependency-check.version} - - - org.apache.maven.plugins - maven-gpg-plugin - ${dep.mvn.gpg.version} - - - - - - org.jacoco - jacoco-maven-plugin - - - default-prepare-agent - - prepare-agent - - - - default-report - - report - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - ${basedir}/../logback-maven.xml - - - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - -
]]>
- + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + +
]]>
+ - false + false - - - - false + + + + false - true - 11 - - -Xdoclint:none - -
- - - attach-javadocs - - jar - - - -
-
-
+ true + 11 + + -Xdoclint:none + + + + + attach-javadocs + + jar + + + + + + - - - - SkipJavadoc - - true - - - true - - - - - org.apache.maven.plugins - maven-surefire-plugin - - ch.exense.commons.test.categories.PerformanceTest - - - - - - - DependencyCheck - - false - - - - + SkipJavadoc + + true + + + true + + + + + org.apache.maven.plugins + maven-surefire-plugin + + ch.exense.commons.test.categories.PerformanceTest + + + + + + + DependencyCheck + + false + + + + - - - - - org.owasp - dependency-check-maven - - HTML,CSV - false - - false - - - - - verify - - - check - - - - - - - - - SignedBuild - - false - - - - - - org.apache.maven.plugins - maven-gpg-plugin - - - sign-artifacts - verify - - sign - - - - --pinentry-mode - loopback - - - - - + + + + + org.owasp + dependency-check-maven + + HTML,CSV + false + + false + + + + + verify + + + check + + + + + + + + + SignedBuild + + false + + + + + + org.apache.maven.plugins + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + --pinentry-mode + loopback + + + + + - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + - - - org.sonatype.central - central-publishing-maven-plugin - 0.8.0 - true - - sonatype - - - - - - + + + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 + true + + sonatype + + + + + +
From 051b7907aa0c9ea5e93def3be5a55fc3974f31f0 Mon Sep 17 00:00:00 2001 From: Christoph Langguth Date: Wed, 26 Nov 2025 14:49:03 +0100 Subject: [PATCH 3/4] SED-4405 Updating ignore list + documentation --- .git-blame-ignore-revs | 1 + README.md | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..30eb6ad --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1 @@ +eb7e29d5e4b012e89f277e7d275381bb1624669f diff --git a/README.md b/README.md index 4336642..d2a4426 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ # exense-commons -This project contains functionality reused across a variety of applications built internally at exense GmbH and is open for external collaboration purposes. +This project contains functionality reused across a variety of applications built internally at exense GmbH and is open +for external collaboration purposes. + +## Instructions for developers + +### Code formatting (indentation and line breaks) + +Use an IDE that supports the `.editorconfig` standard and make sure that it is enabled. This will automatically +apply the recommended settings to all files. + +### Git blame history +To ensure correct handling of "reformat-only" commits, use a recent git version (>= 2.23) with the following +configuration. You can do this per-repository (in that case, omit the `--global` flag), but the filename used +is a de-facto standard, and this way you'll only need to do this once, not for every repository: + +``` +git config --global blame.ignoreRevsFile .git-blame-ignore-revs +``` From 4135dc94eea5dcacfa4d15b2e2095a17673e8c86 Mon Sep 17 00:00:00 2001 From: Christoph Langguth Date: Wed, 26 Nov 2025 14:55:27 +0100 Subject: [PATCH 4/4] SED-4405 Reformat README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d2a4426..e4db758 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Use an IDE that supports the `.editorconfig` standard and make sure that it is e apply the recommended settings to all files. ### Git blame history + To ensure correct handling of "reformat-only" commits, use a recent git version (>= 2.23) with the following configuration. You can do this per-repository (in that case, omit the `--global` flag), but the filename used is a de-facto standard, and this way you'll only need to do this once, not for every repository: