diff --git a/pom.xml b/pom.xml index cbdc15a..8097b3f 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ WebServiceShell edu.iris webserviceshell - 2.4.8-SNAPSHOT + 2.4.9 jar http://iris.edu diff --git a/src/main/java/edu/iris/wss/framework/AppConfigurator.java b/src/main/java/edu/iris/wss/framework/AppConfigurator.java index ff1ec1d..bfc472b 100644 --- a/src/main/java/edu/iris/wss/framework/AppConfigurator.java +++ b/src/main/java/edu/iris/wss/framework/AppConfigurator.java @@ -24,10 +24,13 @@ import edu.iris.wss.provider.IrisProcessMarker; import edu.iris.wss.provider.IrisProcessor; import edu.iris.wss.provider.IrisSingleton; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; @@ -51,11 +54,11 @@ public class AppConfigurator { public static final Logger logger = Logger.getLogger(AppConfigurator.class); - public static final String wssVersion = "2.4.8-SNAPSHOT"; + public static final String wssVersion = "2.4.9"; public static final String wssDigestRealmnameSignature = "wss.digest.realmname"; - private static final String DEFAULT_SERVICE_FILE_NAME = "META-INF/service.cfg"; + protected static final String DEFAULT_SERVICE_FILE_NAME = "META-INF/service.cfg"; public static final String SERVICE_CFG_NAME_SUFFIX = "-service.cfg"; public static final String ENDPOINT_TO_PROPERTIES_DELIMITER = "."; @@ -634,7 +637,8 @@ public static Properties loadPropertiesFile(String configBase, + configFileName); try { - configurationProps.load(new FileInputStream(configFileName)); + InputStream inStream = new FileInputStream(configFileName); + loadWithBackslashFix(configurationProps, inStream); userConfig = true; } catch (IOException ex) { logger.warn("***** could not read cfg file: " + configFileName); @@ -660,7 +664,7 @@ public static Properties loadPropertiesFile(String configBase, logger.info("Attempting to load default application" + " configuration from here: " + defaultCfgName); - configurationProps.load(inStream); + loadWithBackslashFix(configurationProps, inStream); logger.info("Default application properties loaded, file: " + defaultCfgName); @@ -669,6 +673,36 @@ public static Properties loadPropertiesFile(String configBase, return configurationProps; } + public static void loadWithBackslashFix(Properties prop, InputStream is) + throws IOException { + // before loading check for any character after any backslash + // and before the end of line, and remove it + StringBuilder sb = new StringBuilder(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String readLine = ""; + while ((readLine = br.readLine()) != null) { + int idxback = readLine.indexOf("\\"); + int idxEOL = readLine.length() - 1; + + if(idxback > -1) { + if (idxEOL > idxback) { + // fixit + readLine = readLine.substring(0,idxback+1); + int fidxback = readLine.indexOf("\\"); + int fidxEOL = readLine.length() - 1; + } + } + // put back the EOL since readLine removed it + sb.append(readLine).append("\n"); + } + + ByteArrayInputStream sbis = new ByteArrayInputStream( + sb.toString().getBytes("UTF-8")); + + prop.load(sbis); + } + public void loadConfigurationParameters(Properties inputProps) throws Exception { diff --git a/src/test/java/edu/iris/wss/framework/AppConfig_loadPropertiesFileTest.java b/src/test/java/edu/iris/wss/framework/AppConfig_loadPropertiesFileTest.java new file mode 100644 index 0000000..d5baafd --- /dev/null +++ b/src/test/java/edu/iris/wss/framework/AppConfig_loadPropertiesFileTest.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2018 IRIS DMC supported by the National Science Foundation. + * + * This file is part of the Web Service Shell (WSS). + * + * The WSS is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * The WSS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License is available at + * . + ******************************************************************************/ + +package edu.iris.wss.framework; + +import java.io.File; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.util.Properties; +import org.apache.log4j.Logger; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author mike + */ +public class AppConfig_loadPropertiesFileTest { + public static final String THIS_CLASS_NAME = AppConfig_loadPropertiesFileTest.class.getSimpleName(); + public static final Logger LOGGER = Logger.getLogger(THIS_CLASS_NAME); + + private static final String MEDIA_PARAM = "myFORMTname"; + + private static final String SERVICE_CONTEXT = "/loadproptest"; + private static final String ENDPOINT_NAME = "process"; + + private String okFN, notokFN; + + public AppConfig_loadPropertiesFileTest() { + } + + @BeforeClass + public static void setUpClass() { + // define WSS config dir for this test + System.setProperty(Util.WSS_OS_CONFIG_DIR, + "target" + + File.separator + "test-classes" + + File.separator + THIS_CLASS_NAME); + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() throws Exception { + String className = "edu.iris.wss.endpoints.DummyNameEndpoint"; + + String flaw = ""; + okFN = FileCreaterHelper.createFileInWssFolder(SERVICE_CONTEXT, + "-ok"+AppConfigurator.SERVICE_CFG_NAME_SUFFIX, + createServiceCfgStr(ENDPOINT_NAME, className, flaw), + false); + + // insert a hard-to-see problem after a backslash in a list + flaw = " "; + notokFN = FileCreaterHelper.createFileInWssFolder(SERVICE_CONTEXT, + "-notok"+AppConfigurator.SERVICE_CFG_NAME_SUFFIX, + createServiceCfgStr(ENDPOINT_NAME, className, flaw), + false); + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testload_withFlaw() throws Exception { + + // file read from https://www.mkyong.com/java/java-read-a-text-file-line-by-line/ + RandomAccessFile okfile = new RandomAccessFile(okFN, "r"); + FileChannel okchannel = okfile.getChannel(); + RandomAccessFile notokfile = new RandomAccessFile(notokFN, "r"); + FileChannel notokchannel = notokfile.getChannel(); + + // the files should be different, the not ok file should have + // a flaw string after a backslass + assertEquals(true, okchannel.size() != notokchannel.size()); + + okchannel.close(); + okfile.close(); + notokchannel.close(); + notokfile.close(); + + Properties props_ok = + AppConfigurator.loadPropertiesFile(SERVICE_CONTEXT.substring(1), + this.getClass(), "-ok"+AppConfigurator.SERVICE_CFG_NAME_SUFFIX, + AppConfigurator.DEFAULT_SERVICE_FILE_NAME); + + Properties props_notok = + AppConfigurator.loadPropertiesFile(SERVICE_CONTEXT.substring(1), + this.getClass(), "-notok"+AppConfigurator.SERVICE_CFG_NAME_SUFFIX, + AppConfigurator.DEFAULT_SERVICE_FILE_NAME); + + // the properties shoud be the same after the fix is applied when the + // properties are loaded + assertEquals(true, props_ok.keySet().equals(props_notok.keySet())); + } + + @Test + public void testload_withNoBase() throws Exception { + String testDefaultResource = "META-INF/service_withFlaw.cfg"; + + InputStream inStream = this.getClass().getClassLoader() + .getResourceAsStream(testDefaultResource); + + Properties props_notok = new Properties(); + props_notok.load(inStream); + + // this loads the default cfg when it can't find the primary + Properties props_ok = + AppConfigurator.loadPropertiesFile("wrongbase_force_to_default", + this.getClass(), "-ok"+AppConfigurator.SERVICE_CFG_NAME_SUFFIX, + testDefaultResource); + + // the properties should be different after the fixed load + assertEquals(false, props_ok.keySet().equals(props_notok.keySet())); + } + + private static String createServiceCfgStr(String endpointName, + String endpointClass, String flaw) { + String s = String.join("\n", + "# ---------------- globals", + "", + "appName=" + THIS_CLASS_NAME, + "version=0.1", + "", + "corsEnabled=false", + "", + "# LOG4J or JMS", + "loggingMethod=LOG4J", + "", + "# If present, an instance of the singleton class will be created at application start", + "singletonClassName=edu.iris.wss.framework.UnitTestDestroySingleton", + "", + "# ---------------- endpoints", + "", + endpointName + ".endpointClassName=" + endpointClass, + endpointName + ".usageLog", + endpointName + ".postEnabled=true", + endpointName + ".logMiniseedExtents = false", + endpointName + ".use404For204=false", + endpointName + ".formatTypes = \\", + " text: text/plain,\\", + " json: application/json, \\" + flaw, + " miniseed: application/vnd.fdsn.mseed, \\", + " geocsv: text/plain", + endpointName + ".mediaParameter = " + MEDIA_PARAM, + "" + ); + + return s; + } +} diff --git a/src/test/resources/META-INF/service_withFlaw.cfg b/src/test/resources/META-INF/service_withFlaw.cfg new file mode 100644 index 0000000..6b32a1b --- /dev/null +++ b/src/test/resources/META-INF/service_withFlaw.cfg @@ -0,0 +1,59 @@ +# ---------------- globals + +appName=dummy-service +version=dummy-version-2.x + +# CORS is enabled by default, set to false to disable CORS processing +##corsEnabled=false + +# a URL providing information about the application, documentation on usage, etc. +##rootServiceDoc=http://service.iris.edu/ +rootServiceDoc=file:///nofolder/nofolder/nodocfile.html + +# LOG4J or JMS +loggingMethod=LOG4J + +# the default is 60 seconds - time delay between SIGTERM until SIGKILL on command line processes +##sigkillDelay=30 + +# If present, an instance of the singleton class will be created at application start +##singletonClassName=edu.iris.wss.provider.TestSingleton + +# ---------------- endpoints + +# may be a user class that extends IrisProcessor +# by default, set to s set to edu.iris.wss.endpoints.CmdProcessor +# can use edu.iris.wss.endpoints.ProxyResource to return content +dummyEP.endpointClassName=edu.iris.wss.endpoints.CmdProcessor + +dummyEP.handlerProgram=/somefolder/somefolder/some_handler.sh + +# Timeout in seconds for command line processes +dummyEP.handlerTimeout=40 + +# A valid folder with write access for command line processes +dummyEP.handlerWorkingDirectory=/tmp + +# usageLog is true by default, set this to false to disable usage logging +##dummyEP.usageLog=false + +dummyEP.formatTypes = \ + miniseed: application/vnd.fdsn.mseed, \ + mseed: application/vnd.fdsn.mseed, \ + json: application/json, \ + geocsv: text/csv, \zzzpy + text: text/plain,\ + texttree: text/plain,\ + xml: application/xml + +# false by default, true enables POST processing +dummyEP.postEnabled=true + +# false by default, enables additional miniseed processing and logging +##dummyEP.logMiniseedExtents=true + +# false by default, enable this to return HTTP 404 in lieu of 204, NO CONTENT +dummyEP.use404For204=true + +# required when endpointClassName is set to edu.iris.wss.endpoints.ProxyResource +##dummyEP.proxyURL=http://geows.ds.iris.edu/geows-uf/v2/intermagnet-2-swagger.json \ No newline at end of file