From ff85ba443347a5484932426f31d0b945c42ab720 Mon Sep 17 00:00:00 2001 From: Denis Papp <60399457+pappde@users.noreply.github.com> Date: Thu, 22 Aug 2024 08:38:01 -0500 Subject: [PATCH] fix frontendproperties readFile() to work with classpath and update download_custom_buttons_json reference (#10946) * app.properties: add new download_custom_buttons_json to enum and confic_service property list * FrontendProperty enum: fix ordering so list ends on correct line * app.properties example for download_custom_buttons_json fixed - changed example to use absolute path based on default docker config, since classpath: isn't supported - updated documentation - added TODO on readFile() method * merge in docs updates from master * download_custom_buttons: doc tweak * application.properties example updated to match reference * FrontEndProperties: update readFile() to support relative and classpath by leveraging spring ResourceUtils * download_custom_buttons_json: update examples back to "classpath:" * Application.properties example: updated classpath relative path * Frontend Properties readFile: changed classpath to use ClassLoader.getResourceAsStream() instead of ResourceUtils.getFile() - check both the file system and the classpath * readfile: logging, fix style * readFile: split out file/resource search code to new method locateFile() - modified logging to use the verb "Found" --------- Co-authored-by: Denis Papp Co-authored-by: Denis Papp --- .../application.properties-Reference.md | 4 +- .../download_custom_buttons-Reference.md | 7 +- .../FrontendPropertiesServiceImpl.java | 71 ++++++++++++++++--- .../resources/application.properties.EXAMPLE | 2 +- 4 files changed, 67 insertions(+), 17 deletions(-) diff --git a/docs/deployment/customization/application.properties-Reference.md b/docs/deployment/customization/application.properties-Reference.md index cbab1aa5374..2780fe8b6a9 100644 --- a/docs/deployment/customization/application.properties-Reference.md +++ b/docs/deployment/customization/application.properties-Reference.md @@ -800,9 +800,9 @@ enable_study_tags=true|false ``` # Add Custom Buttons to data tables -Custom Buttons can be defined which will conditionally appear in all group comparison data tables (with CopyDownloadControls) to launch a custom URL. This can be used, for example, to launch a software application (that is installed on the user's system) with the data. This configuration can also customize new elements on the Visualize page. It points to a JSON file on the classpath. (See [download_custom_buttons reference](download_custom_buttons-Reference.md)). +Custom Buttons can be defined which will conditionally appear in all group comparison data tables (with CopyDownloadControls) to launch a custom URL. This can be used, for example, to launch a software application (that is installed on the user's system) with the data. This configuration can also customize new elements on the Visualize page. It points to a JSON file. (See [download_custom_buttons reference](download_custom_buttons-Reference.md)). ``` -download_custom_buttons_json=classpath:/custom_buttons/download_custom_button_avm.json +download_custom_buttons_json=classpath:custom_buttons/download_custom_button_avm.json ``` diff --git a/docs/deployment/customization/download_custom_buttons-Reference.md b/docs/deployment/customization/download_custom_buttons-Reference.md index 7050021376d..f6defe75b47 100644 --- a/docs/deployment/customization/download_custom_buttons-Reference.md +++ b/docs/deployment/customization/download_custom_buttons-Reference.md @@ -4,8 +4,7 @@ Custom Buttons can be defined which will conditionally appear in all group compa ## Configuration File -The Custom Buttons are defined in a JSON file in the classpath. Set `download_custom_buttons_json` to refer to the file in the -`application.properties` (See [application.properties reference](application.properties-Reference.md#add-custom-buttons-to-data-tables)). +The Custom Buttons are defined in a JSON file on the classpath. Set `download_custom_buttons_json` to refer to the file (see [application.properties reference](application.properties-Reference.md#add-custom-buttons-to-data-tables)). ## JSON format @@ -67,6 +66,4 @@ To modify software to leverage this: - Modify your software to read data from clipboard. - Modify your software or installer to register a URL protocol to launch. -- Modify your software or installer to install a custom font. - - +- Modify your software or installer to install a custom font. \ No newline at end of file diff --git a/src/main/java/org/cbioportal/service/FrontendPropertiesServiceImpl.java b/src/main/java/org/cbioportal/service/FrontendPropertiesServiceImpl.java index 304ca3853f7..eb2c94c43a2 100644 --- a/src/main/java/org/cbioportal/service/FrontendPropertiesServiceImpl.java +++ b/src/main/java/org/cbioportal/service/FrontendPropertiesServiceImpl.java @@ -8,6 +8,11 @@ import org.springframework.stereotype.Service; import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @@ -291,18 +296,66 @@ private Map cloneProperties() { ); } - private String readFile(String fileName) { - if (fileName != null && !fileName.isEmpty()) { - try (BufferedReader br = Files.newBufferedReader(Paths.get(fileName))) { - return br.lines().map(String::trim).collect(Collectors.joining("")); - } catch (Exception e) { - log.error("Error reading frontend config file: {}", e.getMessage()); - return null; + /** + * Find the file, either on the file system or in a .jar, and return as an InputStream. + * @propertiesFileName: the file path + * @return: a valid InputStream (not null), otherwise throws FileNotFoundException + * TECH: file system locations have precedence over classpath + * REF: based on getResourceStream() in WebServletContextListener.java + */ + private InputStream locateFile(String filePath) throws FileNotFoundException { + // try absolute or relative to working directory + File file = new File(filePath); + if (file.exists()) { + // throws if is a directory or cannot be opened + log.info("Found frontend config file: {}", file.getAbsolutePath()); + return new FileInputStream(file); + } + + // try relative to PORTAL_HOME + String home = System.getenv("PORTAL_HOME"); + if (home != null) { + file = new File(Paths.get(home, filePath).toString()); + if (file.exists()) { + log.info("Found frontend config file: {}", file.getAbsolutePath()); + return new FileInputStream(file); } + } + + // try resource (e.g. app.jar) + InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(filePath); + if (inputStream != null) { + log.info("Found frontend config resource: {}", filePath); + return inputStream; + } else { + throw new FileNotFoundException("File not found in system or classpath: " + filePath); } - return null; } - + + /** + * Read the file, either on the file system or in a .jar, and return the content as a single-line string. + * @propertiesFileName: the file path + */ + private String readFile(String propertiesFileName) { + if (propertiesFileName == null || propertiesFileName.isEmpty()) { + return null; + } + + // strip off classpath prefix and always check all locations (ClassLoader and file system) + String filePath = propertiesFileName.startsWith("classpath:") + ? propertiesFileName.substring("classpath:".length()) + : propertiesFileName; + + try { + InputStream inputStream = locateFile(filePath); + BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); + return br.lines().map(String::trim).collect(Collectors.joining("")); + } catch (Exception e) { + log.error("Error reading frontend config file: {}", e.getMessage()); + return null; + } + } + public String getFrontendUrl(String propertyValue) { String frontendUrlRuntime = env.getProperty("frontend.url.runtime", ""); if (frontendUrlRuntime.length() > 0) { diff --git a/src/main/resources/application.properties.EXAMPLE b/src/main/resources/application.properties.EXAMPLE index b2c80337eae..77846a3cf60 100644 --- a/src/main/resources/application.properties.EXAMPLE +++ b/src/main/resources/application.properties.EXAMPLE @@ -427,7 +427,7 @@ spring.devtools.restart.enabled=false #enable_study_tags=true|false ## Custom Buttons -# download_custom_buttons_json=classpath:/custom_buttons/download_custom_button_avm.json +# download_custom_buttons_json=classpath:custom_buttons/download_custom_button_avm.json # EOL - Do not delete the following lines