selectionPropsToCheck = Arrays
.asList(Shared.RPC_PROP_SUPPRESS_NON_STATIC_FINAL_FIELD_WARNINGS);
@@ -396,8 +401,12 @@ public RebindResult create(TreeLogger logger, GeneratorContext context)
CachedPropertyInformation cpi =
new CachedPropertyInformation(logger, context.getPropertyOracle(), selectionPropsToCheck,
configPropsToCheck);
+ CachedPropertyInformation enhancedClassesCpi =
+ new CachedPropertyInformation(logger, context.getPropertyOracle(), null,
+ enhancedClassesConfigPropsToCheck);
result.putClientData(CACHED_TYPE_INFO_KEY, cti);
result.putClientData(CACHED_PROPERTY_INFO_KEY, cpi);
+ result.putClientData(CACHED_ENHANCED_CLASSES_PROPERTY_INFO_KEY, enhancedClassesCpi);
return result;
} else {
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
index 34f28fea57a..28d5448ee4d 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -98,6 +98,14 @@
*
*
*
+ * Enhanced class handling can be disabled entirely, including the automatic detection of JDO and
+ * JPA annotations, by setting the following configuration property:
+ *
+ *
+ *
+ *
+ *
+ *
* Enhanced classes are checked for the presence of additional serializable
* fields on the server that were not defined in client code as seen by the GWT
* compiler. If it is possible for an instance of such a class to be transmitted
@@ -694,6 +702,8 @@ private static void logSerializableTypes(TreeLogger logger, Set fiel
private final GeneratorContext context;
+ private final boolean enhancedClassesEnabled;
+
private Set enhancedClasses = null;
private PrintWriter logOutputWriter;
@@ -751,6 +761,8 @@ public SerializableTypeOracleBuilder(TreeLogger logger, GeneratorContext context
}
enhancedClasses = Shared.getEnhancedTypes(context.getPropertyOracle());
+ enhancedClassesEnabled =
+ Shared.shouldEnableEnhancedClasses(logger, context.getPropertyOracle());
}
public void addRootType(TreeLogger logger, JType type) {
@@ -867,8 +879,10 @@ public SerializableTypeOracle build(TreeLogger logger) throws UnableToCompleteEx
fieldSerializableTypes.add(type);
}
- if (tic.maybeEnhanced()
- || (enhancedClasses != null && enhancedClasses.contains(type.getQualifiedSourceName()))) {
+ if (enhancedClassesEnabled
+ && (tic.maybeEnhanced()
+ || (enhancedClasses != null
+ && enhancedClasses.contains(type.getQualifiedSourceName())))) {
logger.log(TreeLogger.WARN, "The class " + type.getQualifiedSourceName() + " has " +
"JPA/JDO annotations or is explicitly configured as an enhanced class using the " +
"configuration property rpc.enhancedClasses. This makes the server vulnerable " +
diff --git a/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java b/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
index a1495e8cb1a..503dd5ad10e 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
@@ -36,7 +36,7 @@ public class ServiceInterfaceProxyGenerator extends IncrementalGenerator {
* generator results will be invalidated automatically if they were generated
* by a version of this generator with a different version id.
*/
- private static final long GENERATOR_VERSION_ID = 1L;
+ private static final long GENERATOR_VERSION_ID = 2L;
@Override
public RebindResult generateIncrementally(TreeLogger logger, GeneratorContext ctx,
diff --git a/user/src/com/google/gwt/user/rebind/rpc/Shared.java b/user/src/com/google/gwt/user/rebind/rpc/Shared.java
index 8d4e5ac8f5f..262ddf32ab7 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/Shared.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/Shared.java
@@ -53,6 +53,12 @@ class Shared {
*/
public static final String RPC_ENHANCED_CLASSES = "rpc.enhancedClasses";
+ /**
+ * Single-valued configuration property used to disable all enhanced class handling at compile
+ * time.
+ */
+ public static final String RPC_ENHANCED_CLASSES_ENABLED = "rpc.enhancedClasses.enabled";
+
/**
* Capitalizes a name.
*
@@ -80,6 +86,35 @@ static Set getEnhancedTypes(PropertyOracle propertyOracle) {
}
}
+ /**
+ * Returns whether RPC should generate support for server-enhanced classes.
+ *
+ * @param propertyOracle the property oracle used to access the relevant configuration property
+ * @return whether enhanced class handling is enabled
+ */
+ static boolean shouldEnableEnhancedClasses(TreeLogger logger, PropertyOracle propertyOracle) {
+ try {
+ ConfigurationProperty prop =
+ propertyOracle.getConfigurationProperty(RPC_ENHANCED_CLASSES_ENABLED);
+ if (prop.getValues().size() == 1) {
+ String value = prop.getValues().get(0);
+ if ("true".equalsIgnoreCase(value)) {
+ return true;
+ }
+ if ("false".equalsIgnoreCase(value)) {
+ return false;
+ }
+ }
+ } catch (BadPropertyValueException e) {
+ // Warn below and retain the backwards-compatible behavior.
+ }
+
+ logger.log(TreeLogger.WARN, "The configuration property " + RPC_ENHANCED_CLASSES_ENABLED
+ + " was missing or did not have exactly one 'true' or 'false' value. Is "
+ + "RemoteService.gwt.xml inherited? Enhanced class support will remain enabled.");
+ return true;
+ }
+
static String getStreamReadMethodNameFor(JType type) {
return "read" + getCallSuffix(type);
}
diff --git a/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java b/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
index d4a77086aaf..dcf0de5b533 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
@@ -21,6 +21,7 @@
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.core.ext.BadPropertyValueException;
import com.google.gwt.core.ext.CachedGeneratorResult;
+import com.google.gwt.core.ext.CachedPropertyInformation;
import com.google.gwt.core.ext.ConfigurationProperty;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
@@ -114,6 +115,8 @@ private static void computeShardSize(TreeLogger logger) throws UnableToCompleteE
private final boolean elideTypeNames;
+ private final boolean canReuseCachedFieldSerializers;
+
private final JType[] serializableTypes;
private final SerializableTypeOracle serializationOracle;
@@ -170,9 +173,12 @@ public TypeSerializerCreator(TreeLogger logger, SerializableTypeOracle serializa
}
if (context.isGeneratorResultCachingEnabled()) {
+ canReuseCachedFieldSerializers =
+ cachedEnhancedClassesConfigurationMatches(logger, context);
typesNotUsingCustomFieldSerializers = new HashSet();
customFieldSerializersUsed = new HashSet();
} else {
+ canReuseCachedFieldSerializers = false;
typesNotUsingCustomFieldSerializers = null;
customFieldSerializersUsed = null;
}
@@ -303,6 +309,10 @@ private void createFieldSerializers(TreeLogger logger, GeneratorContext ctx) {
private boolean findReusableCachedFieldSerializerIfAvailable(TreeLogger logger,
GeneratorContext ctx, JType type, JType customFieldSerializer) {
+ if (!canReuseCachedFieldSerializers) {
+ return false;
+ }
+
CachedGeneratorResult lastResult = ctx.getCachedGeneratorResult();
if (lastResult == null || !ctx.isGeneratorResultCachingEnabled()) {
return false;
@@ -352,6 +362,20 @@ private boolean findReusableCachedFieldSerializerIfAvailable(TreeLogger logger,
return foundMatch;
}
+ static boolean cachedEnhancedClassesConfigurationMatches(TreeLogger logger,
+ GeneratorContext context) {
+ CachedGeneratorResult lastResult = context.getCachedGeneratorResult();
+ if (lastResult == null || !context.isGeneratorResultCachingEnabled()) {
+ return false;
+ }
+
+ CachedPropertyInformation cpi =
+ (CachedPropertyInformation) lastResult.getClientData(
+ ProxyCreator.CACHED_ENHANCED_CLASSES_PROPERTY_INFO_KEY);
+ return cpi != null
+ && cpi.checkPropertiesWithPropertyOracle(logger, context.getPropertyOracle());
+ }
+
private String[] getPackageAndClassName(String fullClassName) {
String className = fullClassName;
String packageName = "";
@@ -849,4 +873,3 @@ private void writeTypeMethodsNative(JType type) {
srcWriter.outdent();
}
}
-
diff --git a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
index aef3e8b1302..17dfa598d23 100644
--- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
+++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.user.rebind.rpc;
+import com.google.gwt.core.ext.CachedGeneratorResult;
+import com.google.gwt.core.ext.CachedPropertyInformation;
import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.StubGeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
@@ -41,6 +43,7 @@
import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
import com.google.gwt.dev.javac.testing.impl.StaticJavaResource;
import com.google.gwt.dev.resource.Resource;
+import com.google.gwt.dev.util.UnitTestTreeLogger;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import com.google.gwt.user.rebind.rpc.testcases.client.AbstractSerializableTypes;
import com.google.gwt.user.rebind.rpc.testcases.client.ClassWithTypeParameterThatErasesToObject;
@@ -75,6 +78,7 @@ public class SerializableTypeOracleBuilderTest extends TestCase {
* {@code SerializableTypeOracleBuilder}.
*/
static class MockContext extends StubGeneratorContext {
+ private CachedGeneratorResult cachedGeneratorResult;
private TypeOracle typeOracle;
private PropertyOracle propertyOracle;
@@ -87,11 +91,27 @@ static class MockContext extends StubGeneratorContext {
this.propertyOracle = propertyOracle;
}
+ MockContext(TypeOracle typeOracle, PropertyOracle propertyOracle,
+ CachedGeneratorResult cachedGeneratorResult) {
+ this(typeOracle, propertyOracle);
+ this.cachedGeneratorResult = cachedGeneratorResult;
+ }
+
+ @Override
+ public CachedGeneratorResult getCachedGeneratorResult() {
+ return cachedGeneratorResult;
+ }
+
@Override
public TypeOracle getTypeOracle() {
return typeOracle;
}
+ @Override
+ public boolean isGeneratorResultCachingEnabled() {
+ return cachedGeneratorResult != null;
+ }
+
@Override
public PropertyOracle getPropertyOracle() {
return propertyOracle;
@@ -233,6 +253,47 @@ private static SerializableTypeOracleBuilder createSerializableTypeOracleBuilder
return new SerializableTypeOracleBuilder(logger, new MockContext(to, props));
}
+ private static SerializableTypeOracleBuilder createSerializableTypeOracleBuilder(
+ TreeLogger logger, TypeOracle to, Map> configurationProperties)
+ throws UnableToCompleteException {
+ return new SerializableTypeOracleBuilder(
+ logger, new MockContext(to, createPropertyOracle(configurationProperties)));
+ }
+
+ private static PropertyOracle createPropertyOracle(
+ Map> configurationProperties) {
+ return new BindingProperties(new BindingProperty[0], new String[0],
+ new ConfigurationProperties(configurationProperties)).toPropertyOracle();
+ }
+
+ private static CachedGeneratorResult createCachedGeneratorResult(
+ final CachedPropertyInformation cachedPropertyInformation) {
+ return new CachedGeneratorResult() {
+ @Override
+ public Object getClientData(String key) {
+ if (ProxyCreator.CACHED_ENHANCED_CLASSES_PROPERTY_INFO_KEY.equals(key)) {
+ return cachedPropertyInformation;
+ }
+ return null;
+ }
+
+ @Override
+ public String getResultTypeName() {
+ return null;
+ }
+
+ @Override
+ public long getTimeGenerated() {
+ return 0;
+ }
+
+ @Override
+ public boolean isTypeCached(String typeName) {
+ return false;
+ }
+ };
+ }
+
private static TypeInfo[] getActualTypeInfo(SerializableTypeOracle sto) {
JType[] types = sto.getSerializableTypes();
TypeInfo[] actual = new TypeInfo[types.length];
@@ -969,6 +1030,107 @@ public void testCovariantArrays() throws UnableToCompleteException, NotFoundExce
assertInstantiable(sto, supArray);
}
+ /**
+ * Tests that enhanced class support can be disabled for an entire compilation.
+ */
+ public void testEnhancedClassesCanBeDisabled() throws UnableToCompleteException,
+ NotFoundException {
+ Set resources = new HashSet();
+ addStandardClasses(resources);
+
+ StringBuilder code = new StringBuilder();
+ code.append("import java.io.Serializable;\n");
+ code.append("public class Enhanced implements Serializable {\n");
+ code.append("}\n");
+ resources.add(new StaticJavaResource("Enhanced", code));
+
+ TreeLogger logger = createLogger();
+ TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources);
+ JClassType enhanced = to.getType("Enhanced");
+
+ Map> properties = new HashMap>();
+ properties.put(Shared.RPC_ENHANCED_CLASSES, Collections.singletonList("Enhanced"));
+ properties.put(Shared.RPC_ENHANCED_CLASSES_ENABLED, Collections.singletonList("false"));
+
+ SerializableTypeOracleBuilder sob =
+ createSerializableTypeOracleBuilder(logger, to, properties);
+ sob.addRootType(logger, enhanced);
+ sob.build(logger);
+ assertFalse(enhanced.isEnhanced());
+
+ // The module defaults the property to true so existing applications retain their behavior.
+ properties.put(Shared.RPC_ENHANCED_CLASSES_ENABLED, Collections.singletonList("true"));
+ sob = createSerializableTypeOracleBuilder(logger, to, properties);
+ sob.addRootType(logger, enhanced);
+ sob.build(logger);
+ assertTrue(enhanced.isEnhanced());
+ }
+
+ /**
+ * Tests parsing and backwards-compatible fallback for the enhanced class switch.
+ */
+ public void testEnhancedClassesEnabledPropertyParsing() {
+ Map> properties = new HashMap>();
+ properties.put(Shared.RPC_ENHANCED_CLASSES_ENABLED, Collections.singletonList("TRUE"));
+ assertTrue(Shared.shouldEnableEnhancedClasses(TreeLogger.NULL,
+ createPropertyOracle(properties)));
+
+ properties.put(Shared.RPC_ENHANCED_CLASSES_ENABLED, Collections.singletonList("false"));
+ assertFalse(Shared.shouldEnableEnhancedClasses(TreeLogger.NULL,
+ createPropertyOracle(properties)));
+
+ String warning = "The configuration property " + Shared.RPC_ENHANCED_CLASSES_ENABLED
+ + " was missing or did not have exactly one 'true' or 'false' value. Is "
+ + "RemoteService.gwt.xml inherited? Enhanced class support will remain enabled.";
+ UnitTestTreeLogger.Builder loggerBuilder = new UnitTestTreeLogger.Builder();
+ loggerBuilder.setLowestLogLevel(TreeLogger.WARN);
+ loggerBuilder.expectWarn(warning, null);
+ loggerBuilder.expectWarn(warning, null);
+ loggerBuilder.expectWarn(warning, null);
+ UnitTestTreeLogger logger = loggerBuilder.createLogger();
+
+ properties.clear();
+ assertTrue(Shared.shouldEnableEnhancedClasses(logger, createPropertyOracle(properties)));
+
+ properties.put(Shared.RPC_ENHANCED_CLASSES_ENABLED, Collections.singletonList("invalid"));
+ assertTrue(Shared.shouldEnableEnhancedClasses(logger, createPropertyOracle(properties)));
+
+ properties.put(Shared.RPC_ENHANCED_CLASSES_ENABLED, Arrays.asList("true", "false"));
+ assertTrue(Shared.shouldEnableEnhancedClasses(logger, createPropertyOracle(properties)));
+ logger.assertCorrectLogEntries();
+ }
+
+ /**
+ * Tests that changing the enhanced class property invalidates cached field serializers.
+ */
+ public void testEnhancedClassesPropertyInvalidatesCachedFieldSerializers() {
+ TreeLogger logger = createLogger();
+ Map> properties = new HashMap>();
+ properties.put(Shared.RPC_ENHANCED_CLASSES, Collections.singletonList("Enhanced"));
+ properties.put(Shared.RPC_ENHANCED_CLASSES_ENABLED, Collections.singletonList("true"));
+ PropertyOracle enabledProperties = createPropertyOracle(properties);
+ CachedPropertyInformation cachedPropertyInformation =
+ new CachedPropertyInformation(logger, enabledProperties, null,
+ Arrays.asList(
+ Shared.RPC_ENHANCED_CLASSES, Shared.RPC_ENHANCED_CLASSES_ENABLED));
+ CachedGeneratorResult cachedGeneratorResult =
+ createCachedGeneratorResult(cachedPropertyInformation);
+
+ MockContext context = new MockContext(null, enabledProperties, cachedGeneratorResult);
+ assertTrue(TypeSerializerCreator.cachedEnhancedClassesConfigurationMatches(logger, context));
+
+ properties.put(Shared.RPC_ENHANCED_CLASSES_ENABLED, Collections.singletonList("false"));
+ PropertyOracle disabledProperties = createPropertyOracle(properties);
+ context = new MockContext(null, disabledProperties, cachedGeneratorResult);
+ assertFalse(TypeSerializerCreator.cachedEnhancedClassesConfigurationMatches(logger, context));
+
+ properties.put(Shared.RPC_ENHANCED_CLASSES_ENABLED, Collections.singletonList("true"));
+ properties.put(Shared.RPC_ENHANCED_CLASSES, Collections.singletonList("OtherEnhanced"));
+ PropertyOracle changedEnhancedClasses = createPropertyOracle(properties);
+ context = new MockContext(null, changedEnhancedClasses, cachedGeneratorResult);
+ assertFalse(TypeSerializerCreator.cachedEnhancedClassesConfigurationMatches(logger, context));
+ }
+
/**
* If the query type extends a raw type, be sure to pick up the parameters of
* the raw subertype.