Implement RPC blacklists.
Review by: scottb,spoon
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5324 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardConfigurationProperty.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardConfigurationProperty.java
index ef29748..d035406 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardConfigurationProperty.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardConfigurationProperty.java
@@ -33,8 +33,12 @@
name = p.getName();
values = p.getValues();
- if (values == null || values.size() == 0) {
- throw new IllegalArgumentException("values is null or empty");
+ if (values == null) {
+ throw new IllegalArgumentException("values is null");
+ }
+ if (!p.allowsMultipleValues() && values.size() != 1) {
+ throw new IllegalArgumentException(
+ "p is single-valued but values.size != 1");
}
}
diff --git a/user/src/com/google/gwt/user/RemoteService.gwt.xml b/user/src/com/google/gwt/user/RemoteService.gwt.xml
index 31baf7c..7e89fc1 100644
--- a/user/src/com/google/gwt/user/RemoteService.gwt.xml
+++ b/user/src/com/google/gwt/user/RemoteService.gwt.xml
@@ -35,6 +35,17 @@
-->
<define-configuration-property name="gwt.elideTypeNamesFromRPC" is-multi-valued="false" />
<set-configuration-property name="gwt.elideTypeNamesFromRPC" value="false" />
+
+ <!--
+ Contains regular expressions, optionally prefixed with '+' or '-'.
+ Each type being considered for serialization is tested against the
+ list of expressions in order, and if there is a match it is added to the
+ blacklist (if the prefix is '-' or no prefix is present), or removed (if
+ the prefix is '+'). If multiple entries in the list match a supplied
+ class, then the last one 'wins.' For generic types, the regular
+ expression is applied to just the base class's fully qualified name.
+ -->
+ <define-configuration-property name="rpc.blacklist" is-multi-valued="true" />
<generate-with class="com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator">
<when-type-assignable class="com.google.gwt.user.client.rpc.RemoteService"/>
diff --git a/user/src/com/google/gwt/user/rebind/rpc/BlacklistTypeFilter.java b/user/src/com/google/gwt/user/rebind/rpc/BlacklistTypeFilter.java
new file mode 100644
index 0000000..239d1f5
--- /dev/null
+++ b/user/src/com/google/gwt/user/rebind/rpc/BlacklistTypeFilter.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed 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.
+ */
+
+package com.google.gwt.user.rebind.rpc;
+
+import com.google.gwt.core.ext.BadPropertyValueException;
+import com.google.gwt.core.ext.ConfigurationProperty;
+import com.google.gwt.core.ext.PropertyOracle;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JRealClassType;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+class BlacklistTypeFilter implements TypeFilter {
+
+ private List<Boolean> includeType;
+ private TreeLogger logger;
+ private List<Pattern> typePatterns;
+ private List<String> values;
+
+ public BlacklistTypeFilter(TreeLogger logger, PropertyOracle propertyOracle)
+ throws UnableToCompleteException {
+ this.logger = logger.branch(TreeLogger.DEBUG,
+ "Analyzing RPC blacklist information");
+ try {
+ ConfigurationProperty prop
+ = propertyOracle.getConfigurationProperty("rpc.blacklist");
+
+ values = prop.getValues();
+ int size = values.size();
+ typePatterns = new ArrayList<Pattern>(size);
+ includeType = new ArrayList<Boolean>(size);
+
+ // TODO investigate grouping multiple patterns into a single regex
+ for (String regex : values) {
+ // Patterns that don't start with [+-] are considered to be [-]
+ boolean include = false;
+ // Ignore empty regexes
+ if (regex.length() == 0) {
+ logger.log(TreeLogger.ERROR, "Got empty RPC blacklist entry");
+ throw new UnableToCompleteException();
+ }
+ char c = regex.charAt(0);
+ if (c == '+' || c == '-') {
+ regex = regex.substring(1); // skip initial character
+ include = (c == '+');
+ }
+ try {
+ Pattern p = Pattern.compile(regex);
+ typePatterns.add(p);
+ includeType.add(include);
+
+ logger.log(TreeLogger.DEBUG,
+ "Got RPC blacklist entry '" + regex + "'");
+ } catch (PatternSyntaxException e) {
+ logger.log(TreeLogger.ERROR,
+ "Got malformed RPC blacklist entry '" + regex + "'");
+ throw new UnableToCompleteException();
+ }
+ }
+ } catch (BadPropertyValueException e) {
+ logger.log(TreeLogger.DEBUG, "No RPC blacklist entries present");
+ }
+ }
+
+ public String getName() {
+ return "BlacklistTypeFilter";
+ }
+
+
+ public boolean isAllowed(JClassType type) {
+ String name = getBaseTypeName(type);
+ // For types not handled by getBaseTypeName just return true.
+ if (name == null) {
+ return true;
+ }
+
+ // Process patterns in reverse order for early exit
+ int size = typePatterns.size();
+ for (int idx = size - 1; idx >= 0; idx--) {
+ logger.log(TreeLogger.DEBUG, "Considering RPC rule " + values.get(idx)
+ + " for type " + name);
+ boolean include = includeType.get(idx);
+ Pattern pattern = typePatterns.get(idx);
+ if (pattern.matcher(name).matches()) {
+ if (include) {
+ logger.log(TreeLogger.DEBUG, "Whitelisting type " + name
+ + " according to rule " + values.get(idx));
+ return true;
+ } else {
+ logger.log(TreeLogger.DEBUG, "Blacklisting type " + name
+ + " according to rule " + values.get(idx));
+ return false;
+ }
+ }
+ }
+
+ // Type does not match any pattern, pass it through
+ return true;
+ }
+
+ /**
+ * Returns a simple qualified name for simple types, including classes and
+ * interfaces, parameterized, and raw types. Null is returned for other types
+ * such as arrays and type parameters (e.g., 'E' in java.util.List<E>) because
+ * filtering is meaningless for such types.
+ */
+ private String getBaseTypeName(JClassType type) {
+ JClassType baseType = null;
+
+ if (type instanceof JRealClassType) {
+ baseType = type;
+ } else if (type.isParameterized() != null) {
+ baseType = type.isParameterized().getBaseType();
+ } else if (type.isRawType() != null) {
+ baseType = type.isRawType();
+ }
+
+ return baseType == null ? null : baseType.getQualifiedSourceName();
+ }
+}
+
diff --git a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
index 84a06bb..346e2cd 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
@@ -234,11 +234,17 @@
final PropertyOracle propertyOracle = context.getPropertyOracle();
+ // Load the blacklist/whitelist
+ TypeFilter blacklistTypeFilter = new BlacklistTypeFilter(logger, propertyOracle);
+
// Determine the set of serializable types
SerializableTypeOracleBuilder typesSentFromBrowserBuilder = new SerializableTypeOracleBuilder(
logger, propertyOracle, typeOracle);
+ typesSentFromBrowserBuilder.setTypeFilter(blacklistTypeFilter);
SerializableTypeOracleBuilder typesSentToBrowserBuilder = new SerializableTypeOracleBuilder(
logger, propertyOracle, typeOracle);
+ typesSentToBrowserBuilder.setTypeFilter(blacklistTypeFilter);
+
try {
addRequiredRoots(logger, typeOracle, typesSentFromBrowserBuilder);
addRequiredRoots(logger, typeOracle, typesSentToBrowserBuilder);
@@ -267,9 +273,25 @@
// output.
OutputStream pathInfo = context.tryCreateResource(logger,
serviceIntf.getQualifiedSourceName() + ".rpc.log");
+ PrintWriter writer = new PrintWriter(pathInfo);
+
typesSentFromBrowserBuilder.setLogOutputStream(pathInfo);
- SerializableTypeOracle typesSentFromBrowser = typesSentFromBrowserBuilder.build(logger);
- SerializableTypeOracle typesSentToBrowser = typesSentToBrowserBuilder.build(logger);
+ typesSentToBrowserBuilder.setLogOutputStream(pathInfo);
+
+ writer.write("====================================\n");
+ writer.write("Types potentially sent from browser:\n");
+ writer.write("====================================\n\n");
+ writer.flush();
+ SerializableTypeOracle typesSentFromBrowser
+ = typesSentFromBrowserBuilder.build(logger);
+
+ writer.write("===================================\n");
+ writer.write("Types potentially sent from server:\n");
+ writer.write("===================================\n\n");
+ writer.flush();
+ SerializableTypeOracle typesSentToBrowser
+ = typesSentToBrowserBuilder.build(logger);
+
if (pathInfo != null) {
context.commitResource(logger, pathInfo).setPrivate(true);
}