Merged 5.0.N (5.0.4) to 5.1.N (5.1.1)

123386 adavis: MNT-14295 TransformerConfigLimits Behavior Differs on Java 7 vs 8
      - Fix TransformerConfigLimits.setTransformationLimitsFromProperties(...)
        so that it does not clear the limit (e.g. readLimitTimeMs) when setting
        the max value (e.g. timeoutMs) or the other way around. It is normal to
        have both values defined for the default.transformer. 
      - This has been a problem since 4.2.
      - The problem only came to light with the switch to Java 8, as the order in which
        transformer properties are loaded changed. They were being loaded in MapEntry order.
        The problem did not show up in the unit tests, because they too were impacted by
        the MapEntry order and ironically loaded the properties in the same order.
      - Added unit tests that are not impacted by MapEntry order.
      - Enhanced the debug and toString methods in order to trace the problem.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.1.N/root@123402 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2016-03-09 19:31:53 +00:00
parent 6502f90eaa
commit 76dc3c06f5
5 changed files with 179 additions and 28 deletions

View File

@@ -19,6 +19,7 @@
package org.alfresco.repo.content.transform; package org.alfresco.repo.content.transform;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
@@ -138,4 +139,26 @@ public class DoubleMap<K1, K2, V>
map.put(key2, t); map.put(key2, t);
} }
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
for (Entry<K1, Map<K2, V>> outerEntry: mapMap.entrySet())
{
for (Entry<K2, V> innerEntry: outerEntry.getValue().entrySet())
{
if (sb.length() > 0)
{
sb.append("\n");
}
sb.append(outerEntry.getKey()).
append(", ").
append(innerEntry.getKey()).
append(" = ").
append(innerEntry.getValue());
}
}
return sb.toString();
}
} }

View File

@@ -27,6 +27,7 @@ import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.MimetypeService;
@@ -48,7 +49,7 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
// Entries are added lower down the hierarchy when a search takes place. // Entries are added lower down the hierarchy when a search takes place.
private Map<String, Map<String, DoubleMap<String, String, TransformationOptionLimits>>> limitsMap; private Map<String, Map<String, DoubleMap<String, String, TransformationOptionLimits>>> limitsMap;
// The 'use' value that had properties defined, including null for the default. // The 'use' value that had properties defined, including ANY for the default.
private Set<String> uses; private Set<String> uses;
public TransformerConfigLimits(TransformerProperties transformerProperties, MimetypeService mimetypeService) public TransformerConfigLimits(TransformerProperties transformerProperties, MimetypeService mimetypeService)
@@ -57,7 +58,7 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
} }
/** /**
* Sets the transformer limits created from system properties. * Sets the transformer limits created from properties.
*/ */
private void setLimits(TransformerProperties transformerProperties, MimetypeService mimetypeService) private void setLimits(TransformerProperties transformerProperties, MimetypeService mimetypeService)
{ {
@@ -79,10 +80,25 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
// Populate the limitsMap for each 'use'. // Populate the limitsMap for each 'use'.
for (String use: uses) for (String use: uses)
{ {
// Add the system wide default just in case it is not included, as we always need this one
TransformationOptionLimits limits = getOrCreateTransformerOptionLimits(DEFAULT_TRANSFORMER, ANY, ANY, use);
Collection<TransformerSourceTargetSuffixValue> properties = getPropertiesForUse(use, allUseMap); Collection<TransformerSourceTargetSuffixValue> properties = getPropertiesForUse(use, allUseMap);
setLimits(use, properties);
}
logger.debug(this);
}
/**
* Sets the transformer limits for a single use from properties. Method extracted so that
* it is possible to write a simpler unit test, that changes to the order of the
* properties. The order changed between Java 6, 7 and 8, resulting in MNT-14295. The original
* outer method cannot be used as it creates the list from a map (allUseMap) that it also
* creates and the order of values from that map cannot be controlled from a test.
*/
void setLimits(String use, Collection<TransformerSourceTargetSuffixValue> properties)
{
// Add the system wide default just in case it is not included, as we always need this one
getOrCreateTransformerOptionLimits(DEFAULT_TRANSFORMER, ANY, ANY, use);
TransformationOptionLimits limits;
for (int pass=0; pass<=3; pass++) for (int pass=0; pass<=3; pass++)
{ {
for (TransformerSourceTargetSuffixValue property: properties) for (TransformerSourceTargetSuffixValue property: properties)
@@ -90,6 +106,7 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
int origLevel = getLevel(property.transformerName, property.sourceMimetype); int origLevel = getLevel(property.transformerName, property.sourceMimetype);
if (pass == origLevel) if (pass == origLevel)
{ {
logger.debug(property);
String transformerName = (property.transformerName == null) String transformerName = (property.transformerName == null)
? DEFAULT_TRANSFORMER : property.transformerName; ? DEFAULT_TRANSFORMER : property.transformerName;
limits = getOrCreateTransformerOptionLimits(transformerName, limits = getOrCreateTransformerOptionLimits(transformerName,
@@ -100,7 +117,6 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
} }
} }
} }
}
/** /**
* Returns the 'effective' properties for the given 'use'. * Returns the 'effective' properties for the given 'use'.
@@ -205,6 +221,29 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
return limits; return limits;
} }
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
for (Entry<String, Map<String, DoubleMap<String, String, TransformationOptionLimits>>> useEntry: limitsMap.entrySet())
{
for (Entry<String, DoubleMap<String, String, TransformationOptionLimits>> transformerEntry: useEntry.getValue().entrySet())
{
if (sb.length() > 0)
{
sb.append("\n");
}
sb.append(useEntry.getKey()).
append(", ").
append(transformerEntry.getKey()).
append(" =>\n").
append(transformerEntry.getValue());
}
}
return sb.toString();
}
/** /**
* Creates a new TransformationOptionLimits for the use, transformer and mimetype combination, * Creates a new TransformationOptionLimits for the use, transformer and mimetype combination,
* defaulting values from lower levels. * defaulting values from lower levels.
@@ -259,37 +298,32 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
long l = Long.parseLong(value); long l = Long.parseLong(value);
if (suffix == TransformerConfig.MAX_SOURCE_SIZE_K_BYTES) if (suffix == TransformerConfig.MAX_SOURCE_SIZE_K_BYTES)
{ {
limits.setReadLimitKBytes(-1);
limits.setMaxSourceSizeKBytes(l); limits.setMaxSourceSizeKBytes(l);
} }
else if (suffix == TransformerConfig.TIMEOUT_MS) else if (suffix == TransformerConfig.TIMEOUT_MS)
{ {
limits.setReadLimitTimeMs(-1);
limits.setTimeoutMs(l); limits.setTimeoutMs(l);
} }
else if (suffix == TransformerConfig.MAX_PAGES) else if (suffix == TransformerConfig.MAX_PAGES)
{ {
limits.setPageLimit(-1);
limits.setMaxPages((int)l); limits.setMaxPages((int)l);
} }
else if (suffix == TransformerConfig.READ_LIMIT_K_BYTES) else if (suffix == TransformerConfig.READ_LIMIT_K_BYTES)
{ {
limits.setMaxSourceSizeKBytes(-1);
limits.setReadLimitKBytes(l); limits.setReadLimitKBytes(l);
} }
else if (suffix == TransformerConfig.READ_LIMIT_TIME_MS) else if (suffix == TransformerConfig.READ_LIMIT_TIME_MS)
{ {
limits.setTimeoutMs(-1);
limits.setReadLimitTimeMs(l); limits.setReadLimitTimeMs(l);
} }
else // if (suffix == TransformerConfig.PAGE_LIMIT) else // if (suffix == TransformerConfig.PAGE_LIMIT)
{ {
limits.setMaxPages(-1);
limits.setPageLimit((int)l); limits.setPageLimit((int)l);
} }
} }
private void debug(String msg, String transformerName, String sourceMimetype, String targetMimetype, String use, TransformationOptionLimits limits) private void debug(String msg, String transformerName, String sourceMimetype,
String targetMimetype, String use, TransformationOptionLimits limits)
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
@@ -312,10 +346,9 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
sb.append('.'); sb.append('.');
sb.append(use); sb.append(use);
sb.append('='); sb.append('=');
sb.append(limits.getMaxSourceSizeKBytes()); sb.append(limits);
} }
String line = sb.toString(); String line = sb.toString();
// System.err.println(line);
logger.debug(line); logger.debug(line);
} }
} }

View File

@@ -348,7 +348,7 @@ class TransformerSourceTargetSuffixKey
? "" ? ""
: TransformerConfig.EXTENSIONS+sourceExt+'.'+targetExt)+ : TransformerConfig.EXTENSIONS+sourceExt+'.'+targetExt)+
suffix+ suffix+
(use == null ? "" : TransformerConfig.USE + use); (use == null || ANY.equals(use) ? "" : TransformerConfig.USE + use);
} }
@Override @Override

View File

@@ -221,7 +221,7 @@ public class TransformationOptionLimits implements Serializable
kbytes.append(sb, OPT_MAX_SOURCE_SIZE_K_BYTES, OPT_READ_LIMIT_K_BYTES); kbytes.append(sb, OPT_MAX_SOURCE_SIZE_K_BYTES, OPT_READ_LIMIT_K_BYTES);
pages.append(sb, OPT_MAX_PAGES, OPT_PAGE_LIMIT); pages.append(sb, OPT_MAX_PAGES, OPT_PAGE_LIMIT);
sb.append("}"); sb.append("}");
return sb.length() == 2 ? "" : sb.toString(); return sb.length() == 2 ? "no limits" : sb.toString();
} }
/** /**

View File

@@ -18,10 +18,15 @@
*/ */
package org.alfresco.repo.content.transform; package org.alfresco.repo.content.transform;
import static org.alfresco.repo.content.transform.TransformerConfig.ANY;
import static org.alfresco.repo.content.transform.TransformerPropertyNameExtractorTest.mockMimetypes; import static org.alfresco.repo.content.transform.TransformerPropertyNameExtractorTest.mockMimetypes;
import static org.alfresco.repo.content.transform.TransformerPropertyNameExtractorTest.mockProperties; import static org.alfresco.repo.content.transform.TransformerPropertyNameExtractorTest.mockProperties;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.TransformationOptionLimits; import org.alfresco.service.cmr.repository.TransformationOptionLimits;
import org.junit.Before; import org.junit.Before;
@@ -61,6 +66,44 @@ public class TransformerConfigLimitsTest
"text/plain", "txt"); "text/plain", "txt");
} }
private TransformerConfigLimits newTransformerConfigLimits(String[][] data)
{
// Mock up an extractor with no properties
mockProperties(transformerProperties);
TransformerConfigLimits extractor = new TransformerConfigLimits(transformerProperties, mimetypeService);
// Work out the uses, but keep the order
List<String> uses = new ArrayList<>();
for (int i=0; i < data.length; i++)
{
if (!uses.contains(data[i][4]))
{
uses.add(data[i][4]);
}
}
// Load data for each use - keep the order
for (String use: uses)
{
Collection<TransformerSourceTargetSuffixValue> properties = new ArrayList<>();
for (int i=0; i < data.length; i++)
{
if (use.equals(data[i][4]))
{
properties.add(new TransformerSourceTargetSuffixValue(
data[i][0], data[i][1], data[i][2], data[i][3], data[i][4], data[i][5],
mimetypeService));
}
}
if (properties.size() > 0)
{
extractor.setLimits(use, properties);
}
}
return extractor;
}
@Test @Test
// A value is specified for a transformer and mimetypes // A value is specified for a transformer and mimetypes
public void transformerMimetypesTest() public void transformerMimetypesTest()
@@ -187,6 +230,58 @@ public class TransformerConfigLimitsTest
assertEquals(1, txtToPngLimits.getPageLimit()); assertEquals(1, txtToPngLimits.getPageLimit());
} }
// MNT-14295 With Java 7 the order in which properties were supplied changed from
// what happen with Java 6 and happens with 8. When combined with a bug to do with
// always clearing the max value when setting a limit or the limit when setting
// the max value, the initial map of TransformerConfigLimits would be different.
// Java 7 was used as the runtime for 4.2 and the 5.0 but Java 8 became the default
// from 5.0.1.
// None of the other unit tests in this class failed as a none of them provided
// both max and limit values.
@Test
public void propertyOrderJava7Test()
{
// Load extractor with properties in the order seen when running Java 7
// "content.transformer.default.timeoutMs", "120000",
// "content.transformer.default.readLimitTimeMs", "-1");
extractor = newTransformerConfigLimits(new String[][]
{
{"transformer.default", ANY, ANY, ".timeoutMs", ANY, "120000"},
{"transformer.default", ANY, ANY, ".readLimitTimeMs", ANY, "-1"}
});
TransformationOptionLimits limits = extractor.getLimits(transformer1, "text/plain", "image/png", null);
assertEquals(120000L, limits.getTimeoutMs());
assertEquals(-1L, limits.getReadLimitTimeMs());
}
@Test
public void propertyOrderJava8or6Test()
{
// Load extractor with properties in the order seen when running Java 8
// "content.transformer.default.timeoutMs", "120000",
// "content.transformer.default.readLimitTimeMs", "-1");
extractor = newTransformerConfigLimits(new String[][]
{
{"transformer.default", ANY, ANY, ".readLimitTimeMs", ANY, "-1"},
{"transformer.default", ANY, ANY, ".timeoutMs", ANY, "120000"}
});
TransformationOptionLimits limits = extractor.getLimits(transformer1, "text/plain", "image/png", null);
assertEquals(120000L, limits.getTimeoutMs());
assertEquals(-1L, limits.getReadLimitTimeMs());
}
@Test(expected=IllegalArgumentException.class)
public void bothMaxAndLimitSetTest()
{
mockProperties(transformerProperties,
"content.transformer.default.readLimitTimeMs", "990000",
"content.transformer.default.timeoutMs", "120000");
extractor = new TransformerConfigLimits(transformerProperties, mimetypeService);
}
// --------------------------------------- // ---------------------------------------
@Test @Test