You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by bu...@apache.org on 2017/01/20 21:58:24 UTC
svn commit: r1779683 - in /uima/uimaj/trunk/uimaj-core/src:
main/java/org/apache/uima/resource/ main/java/org/apache/uima/util/impl/
main/resources/org/apache/uima/ test/data/
test/java/org/apache/uima/analysis_engine/impl/
test/resources/TextAnalysisE...
Author: burn
Date: Fri Jan 20 21:58:24 2017
New Revision: 1779683
URL: http://svn.apache.org/viewvc?rev=1779683&view=rev
Log:
UIMA-5273 Check for circular external settings. Added Junit tests and also some for 5208
Added:
uima/uimaj/trunk/uimaj-core/src/test/data/
uima/uimaj/trunk/uimaj-core/src/test/data/testExternalOverride4.settings
Modified:
uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/ResourceConfigurationException.java
uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/impl/Settings_impl.java
uima/uimaj/trunk/uimaj-core/src/main/resources/org/apache/uima/UIMAException_Messages.properties
uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/AnalysisEngine_implTest.java
uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/TestAnnotator2.java
uima/uimaj/trunk/uimaj-core/src/test/resources/TextAnalysisEngineImplTest/testExternalOverride.settings
uima/uimaj/trunk/uimaj-core/src/test/resources/TextAnalysisEngineImplTest/testExternalOverride2.settings
Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/ResourceConfigurationException.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/ResourceConfigurationException.java?rev=1779683&r1=1779682&r2=1779683&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/ResourceConfigurationException.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/ResourceConfigurationException.java Fri Jan 20 21:58:24 2017
@@ -107,6 +107,11 @@ public class ResourceConfigurationExcept
public static final String EXTERNAL_OVERRIDE_NUMERIC_ERROR = "external_override_numeric_error";
/**
+ * Message key for a standard UIMA exception message: External override variable "{0}" has a circular reference to itself
+ */
+ public static final String EXTERNAL_OVERRIDE_CIRCULAR_REFERENCE = "external_override_circular_reference";
+
+ /**
* Creates a new exception with a null message.
*/
public ResourceConfigurationException() {
Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/impl/Settings_impl.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/impl/Settings_impl.java?rev=1779683&r1=1779682&r2=1779683&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/impl/Settings_impl.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/impl/Settings_impl.java Fri Jan 20 21:58:24 2017
@@ -60,6 +60,13 @@ public class Settings_impl implements Se
private Map<String, String> map;
+ // Thread-local map of properties being resolved +for detecting circular references.
+ private ThreadLocal<HashMap<String, Integer>> tlResolving = new ThreadLocal<HashMap<String, Integer>>() {
+ protected synchronized HashMap<String, Integer> initialValue() {
+ return new HashMap<String, Integer>();
+ }
+ };
+
/*
* Regex that matches ${...}
* non-greedy so stops on first '}' -- hence key cannot contain '}'
@@ -176,10 +183,10 @@ public class Settings_impl implements Se
/**
* Look up the value for a property.
- * Perform one substitution pass on ${key} substrings replacing them with the value for key.
- * Recursively evaluate the value to be substituted. NOTE: infinite loops not detected!
- * If the key variable has not been defined, an exception is thrown.
- * To avoid evaluation and get ${key} in the output escape the $ or {
+ * Recursively evaluate the value replacing references ${key} with the value of the key.
+ * Nested references such as ${name-${suffix}} are supported.
+ * Exceptions are thrown for circular references and undefined references.
+ * To avoid evaluation and get ${key} in the output escape the $ or {, e.g. \${key}
* Arrays are returned as a comma-separated string, e.g. "[elem1,elem2]"
* Note: escape characters are not removed as they may affect array separators.
*
@@ -190,36 +197,84 @@ public class Settings_impl implements Se
* @throws ResourceConfigurationException if the value references an undefined property
*/
public String lookUp(String name) throws ResourceConfigurationException {
- String value;
- if ((value = map.get(name)) == null) {
+ return lookUp(name, name);
+ }
+
+ private String lookUp(String from, String name) throws ResourceConfigurationException {
+ // Maintain a set of variables being expanded so can recognize infinite recursion
+ // Needs to be thread-local as multiple threads may be evaluating properties
+ HashMap<String, Integer> resolving = tlResolving.get();
+ if (resolving.containsKey(name)) {
+ System.err.println("Circular evaluation of property: '" + name + "' - definitions are:");
+ for (String s : resolving.keySet()) {
+ System.err.println(resolving.get(s) + ": " + s + " = " + map.get(s));
+ }
+ // Circular reference to external override variable "{0}" when evaluating "{1}"
+ throw new ResourceConfigurationException(ResourceConfigurationException.EXTERNAL_OVERRIDE_CIRCULAR_REFERENCE,
+ new Object[] { name, from });
+ }
+
+ // Add the name for the duration of the lookup
+ resolving.put(name, new Integer(resolving.size()));
+ try {
+ return resolve(from, map.get(name));
+ } finally {
+ resolving.remove(name);
+ }
+ }
+
+ /**
+ * Replace variable references in a string.
+ *
+ * @param value - String to scan for variable references
+ * @return - value with all references resolved and escapes processed
+ * @throws Exception
+ */
+ public String resolve(String value) throws Exception {
+ return unescape(resolve(value, value));
+ }
+
+ private String resolve(String from, String value) throws ResourceConfigurationException {
+ if (value == null) {
return null;
}
Matcher matcher = evalPattern.matcher(value);
+ if (!matcher.find()) {
+ return value;
+ }
StringBuilder result = new StringBuilder(value.length() + 100);
- int lastEnd = 0;
- while (matcher.find()) {
- // Check if the $ is escaped
- if (isEscaped(value, matcher.start())) {
- result.append(value.substring(lastEnd, matcher.start() + 1));
- lastEnd = matcher.start() + 1; // copy the escaped $ and restart after it
- } else {
- result.append(value.substring(lastEnd, matcher.start()));
- lastEnd = matcher.end();
- String key = value.substring(matcher.start() + 2, lastEnd - 1);
- String val = lookUp(key);
- if (val == null) { // External override variable "{0}" references the undefined variable "{1}"
+
+ // If this ${ is escaped then simply remove the \ and expand everything after the ${
+ if (isEscaped(value, matcher.start())) {
+ result.append(value.substring(0, matcher.start() - 1));
+ result.append("${");
+ result.append(resolve(from, value.substring(matcher.start() + 2)));
+ return result.toString();
+ }
+
+ // Find start of variable, expand all that follows, and then look for the end
+ // so that nested entries are supported, e.g. ${name${suffix}}
+ result.append(value.substring(0, matcher.start()));
+ String remainder = resolve(from, value.substring(matcher.start() + 2));
+ int end = remainder.indexOf('}');
+ // If ending } missing leave the ${ as-is
+ // If there is no variable treat as if omitted, i.e. '${}' => ''
+ if (end < 0) {
+ result.append("${");
+ result.append(remainder);
+ } else {
+ String key = remainder.substring(0, end);
+ if (end > 0) {
+ String val = lookUp(from, key);
+ if (val == null) { // Undefined reference to external override variable "{0}" when evaluating "{1}"
throw new ResourceConfigurationException(ResourceConfigurationException.EXTERNAL_OVERRIDE_INVALID,
- new Object[] { name, key });
+ new Object[] { key, from });
}
result.append(val);
}
+ result.append(remainder.substring(end + 1));
}
- if (lastEnd == 0) {
- return value;
- } else {
- result.append(value.substring(lastEnd));
- return result.toString();
- }
+ return result.toString();
}
/**
@@ -227,7 +282,7 @@ public class Settings_impl implements Se
*/
@Override
public String getSetting(String name) throws ResourceConfigurationException {
- String value = lookUp(name);
+ String value = lookUp(name, name);
if (value == null) {
return null;
}
@@ -238,7 +293,7 @@ public class Settings_impl implements Se
throw new ResourceConfigurationException(ResourceConfigurationException.EXTERNAL_OVERRIDE_TYPE_MISMATCH,
new Object[] { name });
}
- return value;
+ return unescape(value); // Process escape characters after checking for array syntax
}
/**
@@ -246,7 +301,7 @@ public class Settings_impl implements Se
*/
@Override
public String[] getSettingArray(String name) throws ResourceConfigurationException {
- String value = lookUp(name);
+ String value = lookUp(name, name);
if (value == null) {
return null;
}
@@ -278,14 +333,14 @@ public class Settings_impl implements Se
int i = 0;
for (String token : tokens) {
if (token != null) {
- result[i++] = escape(token.trim());
+ result[i++] = unescape(token.trim());
}
}
return result;
}
// Final step is to process any escapes by replacing \x by x
- private String escape(String token) {
+ private String unescape(String token) {
int next = token.indexOf('\\');
if (next < 0) {
return token;
Modified: uima/uimaj/trunk/uimaj-core/src/main/resources/org/apache/uima/UIMAException_Messages.properties
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/resources/org/apache/uima/UIMAException_Messages.properties?rev=1779683&r1=1779682&r2=1779683&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/resources/org/apache/uima/UIMAException_Messages.properties (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/resources/org/apache/uima/UIMAException_Messages.properties Fri Jan 20 21:58:24 2017
@@ -130,7 +130,7 @@ config_setting_absent = Configuration se
directory_not_found = Invalid value for parameter "{0}" in component "{1}" -- \
directory "{2}" does not exist.
-external_override_invalid = External override variable "{0}" references the undefined variable "{1}"
+external_override_invalid = Undefined reference to external override variable "{0}" when evaluating "{1}"
external_override_error = Error loading external overrides from "{0}"
@@ -138,6 +138,8 @@ external_override_type_mismatch = Extern
external_override_numeric_error = External override value "{0}" is not an integer
+external_override_circular_reference = Circular reference to external override variable "{0}" when evaluating "{1}"
+
#--------------------------------
#ResourceProcessException
#--------------------------------
Added: uima/uimaj/trunk/uimaj-core/src/test/data/testExternalOverride4.settings
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/test/data/testExternalOverride4.settings?rev=1779683&view=auto
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/test/data/testExternalOverride4.settings (added)
+++ uima/uimaj/trunk/uimaj-core/src/test/data/testExternalOverride4.settings Fri Jan 20 21:58:24 2017
@@ -0,0 +1,19 @@
+ # ***************************************************************
+ # * Licensed to the Apache Software Foundation (ASF) under one
+ # * or more contributor license agreements. See the NOTICE file
+ # * distributed with this work for additional information
+ # * regarding copyright ownership. The ASF licenses this file
+ # * to you 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 License for the
+ # * specific language governing permissions and limitations
+ # * under the License.
+ # ***************************************************************
+test.externalInteger 43
Modified: uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/AnalysisEngine_implTest.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/AnalysisEngine_implTest.java?rev=1779683&r1=1779682&r2=1779683&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/AnalysisEngine_implTest.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/AnalysisEngine_implTest.java Fri Jan 20 21:58:24 2017
@@ -35,6 +35,7 @@ import java.util.Map;
import java.util.Set;
import org.junit.Assert;
+
import junit.framework.TestCase;
import org.apache.uima.Constants;
@@ -63,6 +64,7 @@ import org.apache.uima.cas.text.Annotati
import org.apache.uima.cas.text.AnnotationIndex;
import org.apache.uima.examples.SourceDocumentInformation;
import org.apache.uima.jcas.JCas;
+import org.apache.uima.resource.RelativePathResolver;
import org.apache.uima.resource.Resource;
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.resource.ResourceManager;
@@ -322,9 +324,13 @@ public class AnalysisEngine_implTest ext
ae.destroy();
// descriptor with configuration parameter external overrides
- // implicitly load settings values from the system property UimaExternalSettings
+ // implicitly load settings values from the 3 files in the system property UimaExternalOverrides
+ // Load 1st from filesystem, 2nd from classpath, and 3rd from datapath
+
+ String prevDatapath = System.setProperty(RelativePathResolver.UIMA_DATAPATH_PROP, "src/test/data");
String resDir = "src/test/resources/TextAnalysisEngineImplTest/";
- System.setProperty("UimaExternalOverrides", resDir+"testExternalOverride.settings,"+resDir+"testExternalOverride2.settings");
+ System.setProperty("UimaExternalOverrides",
+ resDir+"testExternalOverride.settings,TextAnalysisEngineImplTest/testExternalOverride2.settings,testExternalOverride4.settings");
in = new XMLInputSource(JUnitExtension.getFile("TextAnalysisEngineImplTest/AnnotatorWithExternalOverrides.xml"));
desc = UIMAFramework.getXMLParser().parseAnalysisEngineDescription(in);
ae1 = new PrimitiveAnalysisEngine_impl();
@@ -344,6 +350,11 @@ public class AnalysisEngine_implTest ext
Integer intValue = (Integer) ae1.getUimaContext().getConfigParameterValue("IntegerParam");
Assert.assertEquals(43, intValue.intValue()); // Will be 42 if external override not defined
System.clearProperty("UimaExternalOverrides");
+ if (prevDatapath == null) {
+ System.clearProperty(RelativePathResolver.UIMA_DATAPATH_PROP);
+ } else {
+ System.setProperty(RelativePathResolver.UIMA_DATAPATH_PROP, prevDatapath);
+ }
ae1.destroy();
Modified: uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/TestAnnotator2.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/TestAnnotator2.java?rev=1779683&r1=1779682&r2=1779683&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/TestAnnotator2.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/TestAnnotator2.java Fri Jan 20 21:58:24 2017
@@ -19,15 +19,9 @@
package org.apache.uima.analysis_engine.impl;
-import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.List;
-import java.util.Map.Entry;
-
-import org.junit.Assert;
import org.apache.uima.UIMAFramework;
import org.apache.uima.UimaContext;
@@ -41,7 +35,7 @@ import org.apache.uima.resource.Resource
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.util.Settings;
import org.apache.uima.util.UimaContextHolderTest;
-import org.apache.uima.util.impl.Settings_impl;
+import org.junit.Assert;
/**
* Annotator class used for testing.
@@ -76,7 +70,6 @@ public class TestAnnotator2 extends CasA
// Note: this annotator launched with external overrides loaded from testExternalOverride2.settings
String contextName = ((UimaContext_ImplBase) aContext).getQualifiedContextName();
if ("/ExternalOverrides/".equals(contextName)) {
- String expected = "Context Holder Test";
String[] actuals = null;
try {
actuals = UimaContextHolder.getContext().getSharedSettingArray("test.externalFloatArray");
@@ -85,8 +78,7 @@ public class TestAnnotator2 extends CasA
}
Assert.assertEquals(0, actuals.length);
- // prefix-suffix Prefix-${suffix}
- // suffix = should be ignored
+ String expected = "Context Holder Test";
String actual = null;
try {
actual = UimaContextHolder.getContext().getSharedSettingValue("context-holder");
@@ -111,7 +103,12 @@ public class TestAnnotator2 extends CasA
// Test a stand-alone settings object
Settings testSettings = UIMAFramework.getResourceSpecifierFactory().createSettings();
- String lines = "foo = ${bar} \n bar : [ok \n OK] \n bad = ${missing}";
+ String lines = "foo = ${bar} \n" +
+ "bar : [ok \n OK] \n" +
+ "bad = ${missing} \n" +
+ "loop1 = one ${loop2} \n" +
+ "loop2 = two ${loop3} \n" +
+ "loop3 = three ${loop1} \n" ;
InputStream is;
try {
is = new ByteArrayInputStream(lines.getBytes("UTF-8"));
@@ -125,6 +122,12 @@ public class TestAnnotator2 extends CasA
} catch (ResourceConfigurationException e) {
System.err.println("Expected exception: " + e.toString());
}
+ try {
+ val = testSettings.lookUp("loop2");
+ Assert.fail("\"loop2\" should create an error");
+ } catch (ResourceConfigurationException e) {
+ System.err.println("Expected exception: " + e.toString());
+ }
} catch (Exception e) {
Assert.fail(e.toString());
}
Modified: uima/uimaj/trunk/uimaj-core/src/test/resources/TextAnalysisEngineImplTest/testExternalOverride.settings
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/test/resources/TextAnalysisEngineImplTest/testExternalOverride.settings?rev=1779683&r1=1779682&r2=1779683&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/test/resources/TextAnalysisEngineImplTest/testExternalOverride.settings (original)
+++ uima/uimaj/trunk/uimaj-core/src/test/resources/TextAnalysisEngineImplTest/testExternalOverride.settings Fri Jan 20 21:58:24 2017
@@ -1,24 +1,23 @@
- # ***************************************************************
- # * Licensed to the Apache Software Foundation (ASF) under one
- # * or more contributor license agreements. See the NOTICE file
- # * distributed with this work for additional information
- # * regarding copyright ownership. The ASF licenses this file
- # * to you 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 License for the
- # * specific language governing permissions and limitations
- # * under the License.
- # ***************************************************************
-prefix Prefix
-suffix Suffix
-test.externalStringArray = [${prefix},\-,${suffix},->,${prefix-suffix}]
-test.externalIntegerArray : [1 , 22 , 3\33,4444]
-test.externalInteger 43
-
+ # ***************************************************************
+ # * Licensed to the Apache Software Foundation (ASF) under one
+ # * or more contributor license agreements. See the NOTICE file
+ # * distributed with this work for additional information
+ # * regarding copyright ownership. The ASF licenses this file
+ # * to you 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 License for the
+ # * specific language governing permissions and limitations
+ # * under the License.
+ # ***************************************************************
+prefix Prefix
+suffix Suffix
+test.externalStringArray = [${prefix},\-,${suffix},->,${prefix-suffix}]
+test.externalIntegerArray : [1 , 22 , 3\33,4444]
+
Modified: uima/uimaj/trunk/uimaj-core/src/test/resources/TextAnalysisEngineImplTest/testExternalOverride2.settings
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/test/resources/TextAnalysisEngineImplTest/testExternalOverride2.settings?rev=1779683&r1=1779682&r2=1779683&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/test/resources/TextAnalysisEngineImplTest/testExternalOverride2.settings (original)
+++ uima/uimaj/trunk/uimaj-core/src/test/resources/TextAnalysisEngineImplTest/testExternalOverride2.settings Fri Jan 20 21:58:24 2017
@@ -1,24 +1,24 @@
- # ***************************************************************
- # * Licensed to the Apache Software Foundation (ASF) under one
- # * or more contributor license agreements. See the NOTICE file
- # * distributed with this work for additional information
- # * regarding copyright ownership. The ASF licenses this file
- # * to you 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 License for the
- # * specific language governing permissions and limitations
- # * under the License.
- # ***************************************************************
-prefix-suffix Prefix-${suffix}
-# The following key should have already been set by an earlier import
-suffix = should be ignored
-# Empty array
-test.externalFloatArray = []
-context-holder = Context Holder Test
+ # ***************************************************************
+ # * Licensed to the Apache Software Foundation (ASF) under one
+ # * or more contributor license agreements. See the NOTICE file
+ # * distributed with this work for additional information
+ # * regarding copyright ownership. The ASF licenses this file
+ # * to you 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 License for the
+ # * specific language governing permissions and limitations
+ # * under the License.
+ # ***************************************************************
+prefix-suffix Prefix-${suffix}
+# The following key should have already been set by an earlier import
+suffix = should be ignored
+# Empty array
+test.externalFloatArray = []
+context-holder = Context Holder Test