System.getProperty as a replacement for replace-with, generate-with

257 views
Skip to first unread message

Colin Alworth

unread,
Jan 29, 2017, 3:46:47 PM1/29/17
to GWT Contributors
I'm trying to keep to good practices in my projects and tools going forward, and seem to either be doing something very wrong, or have hit a possible bug in GWT. Specifically, the selection script seems to look at my one property with three different values, and believes that not only are there three different permutations, but that each has three different soft-permutations:

      unflattenKeylistIntoAnswers(['en'], 'F03147777FEB034474EBDDEB584EAAEB');
      unflattenKeylistIntoAnswers(['es'], 'F03147777FEB034474EBDDEB584EAAEB');
      unflattenKeylistIntoAnswers(['fr'], 'F03147777FEB034474EBDDEB584EAAEB');
      unflattenKeylistIntoAnswers(['en'], 'F03147777FEB034474EBDDEB584EAAEB' + ':1');
      unflattenKeylistIntoAnswers(['es'], 'F03147777FEB034474EBDDEB584EAAEB' + ':1');
      unflattenKeylistIntoAnswers(['fr'], 'F03147777FEB034474EBDDEB584EAAEB' + ':1');
      unflattenKeylistIntoAnswers(['en'], 'F03147777FEB034474EBDDEB584EAAEB' + ':2');
      unflattenKeylistIntoAnswers(['es'], 'F03147777FEB034474EBDDEB584EAAEB' + ':2');
      unflattenKeylistIntoAnswers(['fr'], 'F03147777FEB034474EBDDEB584EAAEB' + ':2');
      strongName = answers[computePropValue('locale')];



I've created a minimal sample project at https://github.com/niloc132/system-property-permtations with a README, copied below for ease of conversation, but here are the relevant files to read:
Module file:
https://github.com/niloc132/system-property-permtations/blob/master/src/main/java/com/colinalworth/gwt/sysprops/SystemPropertyBasedPermutations.gwt.xml
Three html files, each with their own property value set:
https://github.com/niloc132/system-property-permtations/tree/master/src/main/java/com/colinalworth/gwt/sysprops/public
Entrypoint, interface/factory and three "generated" implementations:
https://github.com/niloc132/system-property-permtations/blob/master/src/main/java/com/colinalworth/gwt/sysprops/client/SystemPropertyBasedPermutations.java

Same three html files, hosted with compiled code to be runnable:
https://niloc132.github.io/system-property-permtations/en.html
https://niloc132.github.io/system-property-permtations/fr.html
https://niloc132.github.io/system-property-permtations/es.html

Am I doing this wrong, or is this not expected to work at all without actual <replace-with> or <generate-with> code?

---

This project is a quick attempt at using System.getProperty and generated sources (via APT or some other mechanism) in place of GWT.create and <replace-with> or <generate-with> rules to provide multiple alternate runtime implementations.

There are three HTML files, each of which has a different setting for window.locale, en.html, fr.html, and es.html. Here is en.html:

<!doctype html>
<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <script type="text/javascript" language="javascript">window.locale='en';</script>

    <script type="text/javascript" language="javascript" src="sysprops.nocache.js"></script>
</head>
<body>
</body>
</html>

The module file then describes these three properties, and simple selection script to pick out the window.locale above:

  <define-property name="locale" values="en,fr,es" />
  <property-provider name="locale">
    return window.locale;
  </property-provider>

And finally the entrypoint has a simple switch case to select an implementation based on the value of `System.getProperty("locale"):

        Window.getSelf().alert("Locale is " + System.getProperty("locale"));

        Window.getSelf().alert("Greeting is " + constants.hello());

At present however, the wrong property is read from System.getProperty, and so the wrong implementation is selected, and only one permutation is compiled:

[INFO] --- gwt-maven-plugin:2.8.0:compile (default) @ system-property-permutations ---
[INFO] auto discovered modules [com.colinalworth.gwt.sysprops.SystemPropertyBasedPermutations]
[INFO] Compiling module com.colinalworth.gwt.sysprops.SystemPropertyBasedPermutations
[INFO]    Compiling 1 permutation
[INFO]       Compiling permutation 0...
[INFO]    Compile of permutations succeeded
[INFO]    Compilation succeeded -- 4.529s
[INFO] Linking into /Users/colin/workspace/system-property-permutations/target/system-property-permutations-1.0-SNAPSHOT/sysprops
[INFO]    Link succeeded
[INFO]    Linking succeeded -- 0.206s

The unflattenKeylistIntoAnswers seems to be generated with not only soft permutations where it isn't appropriate, but also seems to think that three permutations are required:

      unflattenKeylistIntoAnswers(['en'], 'F03147777FEB034474EBDDEB584EAAEB');
      unflattenKeylistIntoAnswers(['es'], 'F03147777FEB034474EBDDEB584EAAEB');
      unflattenKeylistIntoAnswers(['fr'], 'F03147777FEB034474EBDDEB584EAAEB');
      unflattenKeylistIntoAnswers(['en'], 'F03147777FEB034474EBDDEB584EAAEB' + ':1');
      unflattenKeylistIntoAnswers(['es'], 'F03147777FEB034474EBDDEB584EAAEB' + ':1');
      unflattenKeylistIntoAnswers(['fr'], 'F03147777FEB034474EBDDEB584EAAEB' + ':1');
      unflattenKeylistIntoAnswers(['en'], 'F03147777FEB034474EBDDEB584EAAEB' + ':2');
      unflattenKeylistIntoAnswers(['es'], 'F03147777FEB034474EBDDEB584EAAEB' + ':2');
      unflattenKeylistIntoAnswers(['fr'], 'F03147777FEB034474EBDDEB584EAAEB' + ':2');
      strongName = answers[computePropValue('locale')];

I think that this results in fr always being selected. Am I doing this wrong, or is System.getProperty only usable for properties which actually have a completely different implementation via <replace-with> or <generate-with>?

James Nelson

unread,
Jan 31, 2017, 1:25:46 PM1/31/17
to GWT Contributors
Here is the code that is doing the selection, in ResolvePermutationDependentValues:


private JExpression propertyValueExpression(JPermutationDependentValue x) {
List<String> propertyValues = props.getConfigurationProperties().getStrings(x.getRequestedValue());

String propertyValue = propertyValues.isEmpty() ? null : Joiner.on(",").join(propertyValues);

if (propertyValue != null) {
// It is a configuration property.
return program.getLiteral(x.getSourceInfo(), propertyValue);
}

if (isSoftPermutationProperty(x.getRequestedValue())) {
JMethod method = getOrCreateSoftPropertyMethod(x.getSourceInfo(), x.getRequestedValue());
return new JMethodCall(x.getSourceInfo(), null, method);
}

propertyValue = commonPropertyAndBindingInfo.getPropertyValue(x.getRequestedValue());

if (propertyValue != null) {
return program.getLiteral(x.getSourceInfo(), propertyValue);
}

return x.getDefaultValueExpression();
}


I'd set a breakpoint there to see where it's getting its values from, and why it leads to combinatorial explosion.

My guess is overloading the property "locale".

Here are some places where it is special cased:

@RunsLocal(requiresProperties = {"locale.queryparam", "locale", "runtime.locales", "locale.cookie"})
public class LocalizableGenerator extends Generator {

sdm Recompiler.java:


if (!binding.isAllowedValue(newValue)) {

String[] allowedValues = binding.getAllowedValues(binding.getRootCondition());
logger.log(TreeLogger.Type.WARN, "property '" + propName +
"' cannot be set to '" + newValue + "'");
logger.log(TreeLogger.Type.INFO, "allowed values: " +
Joiner.on(", ").join(allowedValues));

// See if we can fall back on a reasonable default.
if (allowedValues.length == 1) {
// There is only one possibility, so use it.
newValue = allowedValues[0];
} else if (binding.getName().equals("locale")) {
// TODO: come up with a more general solution. Perhaps fail
// the compile and give the user a way to override the property?
newValue = chooseDefault(binding, "default", "en", "en_US");
} else {
// There is more than one. Continue and possibly compile multiple permutations.
logger.log(TreeLogger.Type.INFO, "continuing without " + propName +
". Sourcemaps may not work.");
return null;
}

AbstractLocalizableImplCreator (#JavaNames):


SelectionProperty localeProp = context.getPropertyOracle()
.getSelectionProperty(logger, "locale");
String defaultLocale = localeProp.getFallbackValue();
if (defaultLocale.length() > 0) {
genLocale = defaultLocale;
}

 
Try it with a different property name, I bet your problem goes away.

James Nelson

unread,
Jan 31, 2017, 1:49:41 PM1/31/17
to GWT Contributors
Yup.  CompilerParameters.gwt.xml:


<define-configuration-property name="js.embedded.properties" is-multi-valued="true"/>
<extend-configuration-property name="js.embedded.properties" value="locale"/>
<extend-configuration-property name="js.embedded.properties" value="user.agent"/>


/**
* Returns the binding property values to be embedded into the initial JavaScript fragment
* for this permutation. (There will be one map for each soft permutation.)
*/
public ImmutableList<ImmutableMap<String, String>> findEmbeddedProperties(TreeLogger logger) {

Set<String> propsWanted = Sets.newTreeSet(getConfigurationProperties().getStrings(
"js.embedded.properties"));


This is new to me, so will let someone else describe in detail, but it seems "local" and "user.agent" are both system-level magic keys you should not be using except for their blessed purpose.

Colin Alworth

unread,
Jan 31, 2017, 2:04:09 PM1/31/17
to GWT Contributors
Alas, changing the property in the java and .gwt.xml to "my.locale" does not result in less incorrect code:

      unflattenKeylistIntoAnswers(['en'], '85A0B45105661378F351C4FA6230BF99');
      unflattenKeylistIntoAnswers(['es'], '85A0B45105661378F351C4FA6230BF99');
      unflattenKeylistIntoAnswers(['fr'], '85A0B45105661378F351C4FA6230BF99');
      unflattenKeylistIntoAnswers(['en'], '85A0B45105661378F351C4FA6230BF99' + ':1');
      unflattenKeylistIntoAnswers(['es'], '85A0B45105661378F351C4FA6230BF99' + ':1');
      unflattenKeylistIntoAnswers(['fr'], '85A0B45105661378F351C4FA6230BF99' + ':1');
      unflattenKeylistIntoAnswers(['en'], '85A0B45105661378F351C4FA6230BF99' + ':2');
      unflattenKeylistIntoAnswers(['es'], '85A0B45105661378F351C4FA6230BF99' + ':2');
      unflattenKeylistIntoAnswers(['fr'], '85A0B45105661378F351C4FA6230BF99' + ':2');
      strongName = answers[computePropValue('my.locale')];

Seems like a good thought though. If there are side effects to reusing those property strings, then those two extend-configuration-property tags should be moved to I18n.gwt.xml and UserAgent.gwt.xml so that User-less GWT code can be written that will be compatible with J2CL.

Anyone else got a hint? Should I just file this as a bug and insert some lame replace-with rules to fool the compiler for the time being? Is this really not actually tested even though it is the "correct" way to manage properties now?
Reply all
Reply to author
Forward
0 new messages