You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by et...@apache.org on 2009/01/14 02:41:25 UTC
svn commit: r734296 - in /incubator/shindig/trunk/java/gadgets/src:
main/java/org/apache/shindig/gadgets/expressions/
main/java/org/apache/shindig/gadgets/preload/
main/java/org/apache/shindig/gadgets/spec/
test/java/org/apache/shindig/gadgets/expressi...
Author: etnu
Date: Tue Jan 13 17:41:24 2009
New Revision: 734296
URL: http://svn.apache.org/viewvc?rev=734296&view=rev
Log:
Applied patch for SHINDIG-814 from Adam Winer.
Added:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/ElException.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/Expression.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/ExpressionContext.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/Expressions.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/GadgetExpressionContext.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/expressions/
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/expressions/ExpressionsTest.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/expressions/GadgetExpressionContextTest.java
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloader.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/PipelinedData.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloaderTest.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/PipelinedDataTest.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ViewTest.java
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/ElException.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/ElException.java?rev=734296&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/ElException.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/ElException.java Tue Jan 13 17:41:24 2009
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package org.apache.shindig.gadgets.expressions;
+
+/**
+ * Exceptions thrown while evaluating expressions.
+ */
+public class ElException extends Exception {
+ public ElException(String message) {
+ super(message);
+ }
+
+ public ElException(Throwable cause) {
+ super(cause);
+ }
+
+ public ElException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/Expression.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/Expression.java?rev=734296&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/Expression.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/Expression.java Tue Jan 13 17:41:24 2009
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+package org.apache.shindig.gadgets.expressions;
+
+/**
+ * A single expression.
+ *
+ * @param <T> return type of the expression.
+ */
+public interface Expression<T> {
+ /**
+ * Evaluates the expression.
+ * @param context the context providing top-level variables.
+ * @return the result of expression evaluation.
+ * @throws ElException if evaluation fails.
+ */
+ T evaluate(ExpressionContext context) throws ElException;
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/ExpressionContext.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/ExpressionContext.java?rev=734296&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/ExpressionContext.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/ExpressionContext.java Tue Jan 13 17:41:24 2009
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+package org.apache.shindig.gadgets.expressions;
+
+/**
+ * Context
+ */
+public interface ExpressionContext {
+ /**
+ * Provide a top-level variable.
+ * @param name name of the top-level variable.
+ * @return the variable, or null if no variable exists with that name.
+ */
+ Object getVariable(String name);
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/Expressions.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/Expressions.java?rev=734296&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/Expressions.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/Expressions.java Tue Jan 13 17:41:24 2009
@@ -0,0 +1,256 @@
+/*
+ * 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.
+ */
+package org.apache.shindig.gadgets.expressions;
+
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+/**
+ * Implementation of the small subset of JSP EL that OpenSocial supports:
+ * - String concatenation
+ * - The "." operator
+ * - Limited type coercion
+ */
+public class Expressions {
+ /** Constant for the start of an expression */
+ private static final String EXPRESSION_START = "${";
+ /** Constant for the end of an expression */
+ private static final char EXPRESSION_END = '}';
+
+ /**
+ * Parse a string into an Expression object.
+ * @param <T> type of the expression
+ * @param text the text in the expression
+ * @param type type of object the expression will return
+ * @throws ElException if errors are found in the expression while parsing.
+ * Unterminated expressions, empty expressions, and constant values that
+ * cannot be properly coerced will result in exceptions.
+ */
+ public static <T> Expression<T> parse(String text, Class<T> type) throws ElException {
+ Preconditions.checkNotNull(text);
+ Preconditions.checkNotNull(type);
+
+ // Check for constant text (no EL), or one big EL expression
+ int nextExpressionIndex = nextExpression(text, 0);
+ if (nextExpressionIndex < 0) {
+ T value = coerce(text, type);
+ return new ConstantExpression<T>(value);
+ } else if (nextExpressionIndex == 0 && endOfExpression(text, 2) == text.length() - 1) {
+ return parseEL(text, 2, text.length() - 1, type);
+ }
+
+ // String concatenation case; find each expression
+ List<Expression<String>> expressions = Lists.newArrayList();
+ int start = 0;
+ while (nextExpressionIndex >= 0) {
+ // Add a constant expression if needed
+ if (start < nextExpressionIndex) {
+ String constantText = text.substring(start, nextExpressionIndex);
+ expressions.add(new ConstantExpression<String>(constantText));
+ }
+
+ int endOfExpression = endOfExpression(text, nextExpressionIndex + 2);
+ if (endOfExpression < 0) {
+ throw new ElException("Unterminated expression in " + text);
+ }
+
+ expressions.add(parseEL(text, nextExpressionIndex + 2, endOfExpression, String.class));
+ start = endOfExpression + 1;
+ nextExpressionIndex = nextExpression(text, start);
+ }
+
+ if (start < text.length()) {
+ String constantText = text.substring(start);
+ expressions.add(new ConstantExpression<String>(constantText));
+ }
+
+ return new ConcatExpression<T>(expressions, type);
+ }
+
+ /**
+ * Parses an EL expression within a string.
+ * @throws ElException
+ */
+ private static <T> Expression<T> parseEL(final String text, int from, int to,
+ final Class<T> type) throws ElException {
+ if (from == to) {
+ throw new ElException("Empty expression in " + text);
+ }
+
+ // TODO: the spec only describes support for "a.b", not "a.b.c.d". Update
+ // the spec, or limit this function?
+ final String[] segments = text.substring(from, to).split("\\.");
+ return new Expression<T>() {
+
+ public T evaluate(ExpressionContext context) throws ElException {
+ Object value = context.getVariable(segments[0]);
+ for (int i = 1; i < segments.length; i++) {
+ if (value == null) {
+ throw new ElException("Could not find property \"" + segments[i - 1] + "\" in \""
+ + text + "\"");
+ }
+ value = getProperty(value, segments[i]);
+ }
+
+ return coerce(value, type);
+ }
+
+ };
+ }
+
+ private static Object getProperty(Object value, String propertyName) throws ElException {
+ if (value instanceof Map) {
+ Map<?, ?> map = (Map<?, ?>) value;
+ return map.get(propertyName);
+ } else if (value instanceof JSONObject) {
+ return ((JSONObject) value).opt(propertyName);
+ }
+
+ throw new ElException("Unsupported property parent type " + value.getClass());
+ }
+
+ /**
+ * Hardcoded Object-to-Object coercion logic.
+ * - Strings are parsed with toString().
+ * - Integers are parsed from numbers with intValue(), and with toString() and
+ * Integer.parseInt() for all other types.
+ * - Booleans are false if they are numeric and equal to 0, if they are
+ * Boolean.FALSE, or if they are case-insensitive equal to "false".
+ * - Arrays are parsed with comma separators.
+ *
+ */
+ static <T> T coerce(Object value, Class<T> type) throws ElException {
+ if (value == null) {
+ return null;
+ }
+
+ if (type == String.class) {
+ @SuppressWarnings("unchecked")
+ T string = (T) value.toString();
+ return string;
+ } else if (type == Integer.class) {
+ int intValue;
+ if (value instanceof Number) {
+ intValue = ((Number) value).intValue();
+ } else {
+ try {
+ intValue = Integer.parseInt(value.toString());
+ } catch (NumberFormatException nfe) {
+ throw new ElException(nfe);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ T integer = (T) Integer.valueOf(intValue);
+ return integer;
+ } else if (type == JSONArray.class) {
+ JSONArray array;
+ if (value instanceof JSONArray) {
+ array = (JSONArray) value;
+ } else {
+ array = new JSONArray();
+ StringTokenizer tokenizer = new StringTokenizer(value.toString(), ",");
+ while (tokenizer.hasMoreTokens()) {
+ array.put(tokenizer.nextToken());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ T t = (T) array;
+ return t;
+ } else if (type == Boolean.class) {
+ boolean boolValue;
+ // TODO: spec question, does this coercion make sense?
+ if (value instanceof Number) {
+ boolValue = ((Number) value).intValue() != 0;
+ } else if (value instanceof Boolean) {
+ boolValue = Boolean.TRUE.equals(value);
+ } else {
+ // TODO: especially this case...
+ boolValue = !"false".equalsIgnoreCase(value.toString());
+ }
+
+ @SuppressWarnings("unchecked")
+ T t = (T) Boolean.valueOf(boolValue);
+ return t;
+ }
+
+ // Fallback, see if the type is already correct
+ try {
+ return type.cast(value);
+ } catch (ClassCastException cce) {
+ throw new ElException("Could not cast " + value + " to " + type.getCanonicalName(),
+ cce);
+ }
+ }
+
+ /** Find the start of the next expression */
+ private static int nextExpression(String text, int after) {
+ // TODO: JSP EL supports escaping. The Pipelining spec does not.
+ // Add detection of "\${" to the spec?
+ return text.indexOf(EXPRESSION_START, after);
+ }
+
+ /** Find the end of the current expression */
+ private static int endOfExpression(String text, int after) {
+ // TODO: support escaping
+ return text.indexOf(EXPRESSION_END, after);
+ }
+
+ /** Expression class for constant values */
+ private static class ConstantExpression<T> implements Expression<T> {
+ private final T value;
+
+ public ConstantExpression(T value) {
+ this.value = value;
+ }
+
+ public T evaluate(ExpressionContext context) {
+ return value;
+ }
+ }
+
+ /** Expression class for string concatenation */
+ private static class ConcatExpression<T> implements Expression<T> {
+
+ private final List<Expression<String>> expressions;
+ private final Class<T> type;
+
+ public ConcatExpression(List<Expression<String>> expressions, Class<T> type) {
+ this.expressions = expressions;
+ this.type = type;
+ }
+
+ public T evaluate(ExpressionContext context) throws ElException {
+ StringBuilder builder = new StringBuilder();
+ for (Expression<String> expression : expressions) {
+ builder.append(expression.evaluate(context));
+ }
+
+ return coerce(builder.toString(), type);
+ }
+ }
+}
\ No newline at end of file
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/GadgetExpressionContext.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/GadgetExpressionContext.java?rev=734296&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/GadgetExpressionContext.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/expressions/GadgetExpressionContext.java Tue Jan 13 17:41:24 2009
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package org.apache.shindig.gadgets.expressions;
+
+import org.apache.shindig.gadgets.GadgetContext;
+
+import java.util.logging.Logger;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Implementation of ExpressionContext for gadgets. Supports
+ * two variables, "UserPrefs" and "ViewParams".
+ */
+public class GadgetExpressionContext implements ExpressionContext {
+ private static final Logger log = Logger.getLogger(GadgetExpressionContext.class.getName());
+
+ /** GadgetContext */
+ private final GadgetContext context;
+
+ /** Lazily evaluated view params */
+ private JSONObject viewParams;
+
+ public GadgetExpressionContext(GadgetContext context) {
+ this.context = context;
+ }
+
+ public Object getVariable(String name) {
+ if ("UserPrefs".equals(name)) {
+ return context.getUserPrefs().getPrefs();
+ } else if ("ViewParams".equals(name)) {
+ return getViewParams();
+ }
+
+ return null;
+ }
+
+ /** Instantiate and return a JSONObject off of view params */
+ private JSONObject getViewParams() {
+ if (viewParams == null) {
+ String params = context.getParameter("view-params");
+ if (params == null || "".equals(params)) {
+ viewParams = new JSONObject();
+ } else {
+ try {
+ viewParams = new JSONObject(params);
+ } catch (JSONException e) {
+ log.warning("Could not parse " + params);
+ viewParams = new JSONObject();
+ }
+ }
+ }
+
+ return viewParams;
+ }
+
+}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloader.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloader.java?rev=734296&r1=734295&r2=734296&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloader.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloader.java Tue Jan 13 17:41:24 2009
@@ -28,6 +28,7 @@
import org.apache.shindig.gadgets.http.RequestPipeline;
import org.apache.shindig.gadgets.spec.GadgetSpec;
import org.apache.shindig.gadgets.spec.Preload;
+import org.apache.shindig.gadgets.spec.RequestAuthenticationInfo;
import org.apache.shindig.gadgets.spec.View;
import com.google.common.collect.ImmutableMap;
@@ -69,7 +70,7 @@
&& phase == PreloaderService.PreloadPhase.PROXY_FETCH) {
List<Callable<PreloadedData>> preloadList = Lists.newArrayList();
- Map<String, Object> socialPreloads = view.getPipelinedData().getSocialPreloads();
+ Map<String, Object> socialPreloads = view.getPipelinedData().getSocialPreloads(context);
// Load any social preloads into a JSONArray for delivery to
// JsonRpcServlet
@@ -83,9 +84,11 @@
preloadList.add(preloader);
}
- Map<String, Preload> httpPreloads = view.getPipelinedData().getHttpPreloads();
+ Map<String, RequestAuthenticationInfo> httpPreloads =
+ view.getPipelinedData().getHttpPreloads(context);
if (!httpPreloads.isEmpty()) {
- for (Map.Entry<String, Preload> httpPreloadEntry : httpPreloads.entrySet()) {
+ for (Map.Entry<String, RequestAuthenticationInfo> httpPreloadEntry
+ : httpPreloads.entrySet()) {
preloadList.add(new HttpPreloadTask(context, httpPreloadEntry.getValue(),
httpPreloadEntry.getKey()));
}
@@ -147,10 +150,10 @@
// A task for preloading os:MakeRequest
class HttpPreloadTask implements Callable<PreloadedData> {
private final GadgetContext context;
- private final Preload preload;
+ private final RequestAuthenticationInfo preload;
private final String key;
- public HttpPreloadTask(GadgetContext context, Preload preload, String key) {
+ public HttpPreloadTask(GadgetContext context, RequestAuthenticationInfo preload, String key) {
this.context = context;
this.preload = preload;
this.key = key;
@@ -158,6 +161,23 @@
public PreloadedData call() throws Exception {
HttpRequest request = HttpPreloader.newHttpRequest(context, preload);
+ String refreshIntervalStr = preload.getAttributes().get("refreshInterval");
+ if (refreshIntervalStr != null) {
+ try {
+ int refreshInterval = Integer.parseInt(refreshIntervalStr);
+ request.setCacheTtl(refreshInterval);
+ } catch (NumberFormatException nfe) {
+ // Ignore, and use the HTTP response interval
+ }
+ }
+
+ // TODO: support non GET/POST methods
+ String method = preload.getAttributes().get("method");
+ if (method != null) {
+ request.setMethod(method);
+ }
+
+ // TODO: support params
return new Data(requestPipeline.execute(request));
}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/PipelinedData.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/PipelinedData.java?rev=734296&r1=734295&r2=734296&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/PipelinedData.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/PipelinedData.java Tue Jan 13 17:41:24 2009
@@ -17,38 +17,49 @@
*/
package org.apache.shindig.gadgets.spec;
-import com.google.common.collect.Maps;
import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.common.xml.XmlException;
import org.apache.shindig.gadgets.AuthType;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.expressions.ElException;
+import org.apache.shindig.gadgets.expressions.Expression;
+import org.apache.shindig.gadgets.expressions.ExpressionContext;
+import org.apache.shindig.gadgets.expressions.Expressions;
+import org.apache.shindig.gadgets.expressions.GadgetExpressionContext;
import org.apache.shindig.gadgets.variables.Substitutions;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
-import java.util.Collections;
-import java.util.Map;
-import java.util.StringTokenizer;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
/**
* Parsing code for <os:*> elements.
*/
public class PipelinedData {
-
- private final Map<String, Object> socialPreloads;
- private final Map<String, Preload> httpPreloads;
+ private final Map<String, SocialData> socialPreloads;
+ private final Map<String, HttpData> httpPreloads;
private boolean needsViewer;
private boolean needsOwner;
public static final String OPENSOCIAL_NAMESPACE = "http://ns.opensocial.org/2008/markup";
-
public PipelinedData(Element element, Uri base) throws SpecParserException {
- Map<String, Object> socialPreloads = Maps.newHashMap();
- Map<String, Preload> httpPreloads = Maps.newHashMap();
+ Map<String, SocialData> socialPreloads = Maps.newHashMap();
+ Map<String, HttpData> httpPreloads = Maps.newHashMap();
// TODO: extract this loop into XmlUtils.getChildrenWithNamespace
for (Node node = element.getFirstChild(); node != null; node = node.getNextSibling()) {
@@ -57,7 +68,7 @@
}
Element child = (Element) node;
- // Ignore elements not in the namesapce
+ // Ignore elements not in the namespace
if (!OPENSOCIAL_NAMESPACE.equals(child.getNamespaceURI())) {
continue;
}
@@ -77,18 +88,19 @@
} else if ("OwnerRequest".equals(elementName)) {
socialPreloads.put(key, createOwnerRequest(child));
} else if ("PersonAppDataRequest".equals(elementName)) {
+ // TODO: delete when 0.9 app data retrieval is supported
socialPreloads.put(key, createPersonAppDataRequest(child));
} else if ("ActivitiesRequest".equals(elementName)) {
socialPreloads.put(key, createActivityRequest(child));
} else if ("MakeRequest".equals(elementName)) {
httpPreloads.put(key, createMakeRequest(child, base));
} else {
- // TODO: This is wrong - the spec should parse, but should preload notImplemented
+ // TODO: This is wrong - the spec should parse, but should preload
+ // notImplemented
throw new SpecParserException("Unknown element <os:" + elementName + ">");
}
- // TODO: implement DataRequest
- } catch (JSONException je) {
- throw new SpecParserException(new XmlException(je));
+ } catch (ElException ele) {
+ throw new SpecParserException(new XmlException(ele));
}
}
@@ -97,21 +109,21 @@
}
private PipelinedData(PipelinedData socialData, Substitutions substituter) {
- Map<String, Object> socialPreloads = Maps.newHashMap();
- Map<String, Preload> httpPreloads = Maps.newHashMap();
+ Map<String, SocialData> socialPreloads = Maps.newHashMap();
+ Map<String, HttpData> httpPreloads = Maps.newHashMap();
// TODO: support hangman substitutions for social preloads?
socialPreloads.putAll(socialData.socialPreloads);
-
- for (Map.Entry<String, Preload> httpPreload : socialData.httpPreloads.entrySet()) {
+
+ for (Map.Entry<String, HttpData> httpPreload : socialData.httpPreloads.entrySet()) {
httpPreloads.put(httpPreload.getKey(), httpPreload.getValue().substitute(substituter));
}
this.socialPreloads = Collections.unmodifiableMap(socialPreloads);
this.httpPreloads = Collections.unmodifiableMap(httpPreloads);
- }
-
+ }
+
/**
* Allows the creation of a view from an existing view so that localization
* can be performed.
@@ -120,12 +132,36 @@
return new PipelinedData(this, substituter);
}
- public Map<String, Object> getSocialPreloads() {
- return socialPreloads;
+ public Map<String, Object> getSocialPreloads(GadgetContext context) {
+ Map<String, Object> evaluatedPreloads = Maps.newHashMapWithExpectedSize(
+ socialPreloads.size());
+ ExpressionContext expressionContext = new GadgetExpressionContext(context);
+ for (Map.Entry<String, SocialData> preload : socialPreloads.entrySet()) {
+ try {
+ evaluatedPreloads.put(preload.getKey(), preload.getValue().toJson(expressionContext));
+ } catch (ElException e) {
+ // TODO: Handle!?!
+ throw new RuntimeException(e);
+ }
+ }
+
+ return evaluatedPreloads;
}
- public Map<String, Preload> getHttpPreloads() {
- return httpPreloads;
+ public Map<String, RequestAuthenticationInfo> getHttpPreloads(GadgetContext context) {
+ Map<String, RequestAuthenticationInfo> evaluatedPreloads = Maps.newHashMapWithExpectedSize(
+ httpPreloads.size());
+ ExpressionContext expressionContext = new GadgetExpressionContext(context);
+ for (Map.Entry<String, HttpData> preload : httpPreloads.entrySet()) {
+ try {
+ evaluatedPreloads.put(preload.getKey(), preload.getValue().evaluate(expressionContext));
+ } catch (ElException e) {
+ // TODO: Handle!?!
+ throw new RuntimeException(e);
+ }
+ }
+
+ return evaluatedPreloads;
}
public boolean needsViewer() {
@@ -137,86 +173,81 @@
}
/** Handle the os:PeopleRequest element */
- private Object createPeopleRequest(Element child) throws JSONException {
- JSONObject params = new JSONObject();
- copyAttribute("groupId", child, params);
- copyArrayAttribute("userId", child, params, true);
- copyArrayAttribute("personId", child, params, true);
-
- copyIntegerAttribute("startIndex", child, params);
- copyIntegerAttribute("count", child, params);
- copyAttribute("sortBy", child, params);
- copyAttribute("sortOrder", child, params);
- copyAttribute("filterBy", child, params);
- copyAttribute("filterOperation", child, params);
- copyAttribute("filterValue", child, params);
- copyArrayAttribute("fields", child, params, false);
-
- JSONObject peopleRequest = new JSONObject();
- peopleRequest.put("method", "people.get");
- peopleRequest.put("params", params);
- peopleRequest.put("id", child.getAttribute("key"));
- return peopleRequest;
+ private SocialData createPeopleRequest(Element child) throws ElException {
+ SocialData expression = new SocialData(child.getAttribute("key"), "people.get");
+
+ copyAttribute("groupId", child, expression, String.class);
+ copyAttribute("userId", child, expression, JSONArray.class);
+ updateUserArrayState("userId", child);
+ copyAttribute("personId", child, expression, JSONArray.class);
+ updateUserArrayState("personId", child);
+
+ copyAttribute("startIndex", child, expression, Integer.class);
+ copyAttribute("count", child, expression, Integer.class);
+ copyAttribute("sortBy", child, expression, String.class);
+ copyAttribute("sortOrder", child, expression, String.class);
+ copyAttribute("filterBy", child, expression, String.class);
+ copyAttribute("filterOperation", child, expression, String.class);
+ copyAttribute("filterValue", child, expression, String.class);
+ copyAttribute("fields", child, expression, JSONArray.class);
+
+ return expression;
}
/** Handle the os:ViewerRequest element */
- private Object createViewerRequest(Element child) throws JSONException {
+ private SocialData createViewerRequest(Element child) throws ElException {
return createPersonRequest(child, "@viewer");
}
/** Handle the os:OwnerRequest element */
- private Object createOwnerRequest(Element child) throws JSONException {
+ private SocialData createOwnerRequest(Element child) throws ElException {
return createPersonRequest(child, "@owner");
}
- private Object createPersonRequest(Element child, String userId) throws JSONException {
- JSONObject params = new JSONObject();
+ private SocialData createPersonRequest(Element child, String userId) throws ElException {
+ SocialData expression = new SocialData(child.getAttribute("key"), "people.get");
- params.put("userId", new JSONArray().put(userId));
+ expression.addProperty("userId", userId, JSONArray.class);
updateUserState(userId);
- copyArrayAttribute("fields", child, params, false);
+ copyAttribute("fields", child, expression, JSONArray.class);
- JSONObject peopleRequest = new JSONObject();
- peopleRequest.put("method", "people.get");
- peopleRequest.put("params", params);
- peopleRequest.put("id", child.getAttribute("key"));
- return peopleRequest;
+ return expression;
}
/** Handle the os:PersonAppDataRequest element */
- private Object createPersonAppDataRequest(Element child) throws JSONException {
- JSONObject params = new JSONObject();
- copyAttribute("groupId", child, params);
- copyArrayAttribute("userId", child, params, true);
- copyAttribute("appId", child, params);
- copyArrayAttribute("fields", child, params, false);
-
- JSONObject appDataRequest = new JSONObject();
- appDataRequest.put("method", "appdata.get");
- appDataRequest.put("params", params);
- appDataRequest.put("id", child.getAttribute("key"));
- return appDataRequest;
+ private SocialData createPersonAppDataRequest(Element child) throws ElException {
+ SocialData expression = new SocialData(child.getAttribute("key"), "appdata.get");
+
+ copyAttribute("groupId", child, expression, String.class);
+ copyAttribute("userId", child, expression, JSONArray.class);
+ updateUserArrayState("userId", child);
+ copyAttribute("appId", child, expression, String.class);
+ copyAttribute("fields", child, expression, JSONArray.class);
+
+ return expression;
}
/** Handle the os:ActivitiesRequest element */
- private Object createActivityRequest(Element child) throws JSONException {
- JSONObject params = new JSONObject();
- copyAttribute("groupId", child, params);
- copyArrayAttribute("userId", child, params, true);
- copyAttribute("appId", child, params);
- copyArrayAttribute("activitityId", child, params, false);
- copyArrayAttribute("fields", child, params, false);
-
- JSONObject appDataRequest = new JSONObject();
- appDataRequest.put("method", "activities.get");
- appDataRequest.put("params", params);
- appDataRequest.put("id", child.getAttribute("key"));
- return appDataRequest;
+ private SocialData createActivityRequest(Element child) throws ElException {
+ SocialData expression = new SocialData(child.getAttribute("key"), "activities.get");
+
+ copyAttribute("groupId", child, expression, String.class);
+ copyAttribute("userId", child, expression, JSONArray.class);
+ updateUserArrayState("userId", child);
+ copyAttribute("appId", child, expression, String.class);
+ // TODO: should be activityIds?
+ copyAttribute("activityId", child, expression, JSONArray.class);
+ copyAttribute("fields", child, expression, JSONArray.class);
+
+ return expression;
}
/** Handle an os:MakeRequest element */
- private Preload createMakeRequest(Element child, Uri base) throws SpecParserException {
- Preload preload = new Preload(child, base);
+ private HttpData createMakeRequest(Element child, Uri base) throws ElException {
+ HttpData data = new HttpData(child, base);
+
+ /* TODO: check auth type, and sign-by-owner/viewer, once spec agrees
+ * to remove support for EL on @authz and @sign_*.
if (preload.getAuthType() != AuthType.NONE) {
if (preload.isSignOwner()) {
needsOwner = true;
@@ -225,46 +256,26 @@
if (preload.isSignViewer()) {
needsViewer = true;
}
- }
-
- return preload;
- }
+ }*/
- private void copyAttribute(String name, Element element, JSONObject jsonObject)
- throws JSONException {
- if (element.hasAttribute(name)) {
- jsonObject.put(name, element.getAttribute(name));
- }
+ return data;
}
- private void copyIntegerAttribute(String name, Element element, JSONObject jsonObject)
- throws JSONException {
+ private void copyAttribute(String name, Element element, SocialData expression, Class<?> type)
+ throws ElException {
if (element.hasAttribute(name)) {
- String valueStr = element.getAttribute(name);
- try {
- int value = Integer.parseInt(valueStr);
- jsonObject.put(name, value);
- } catch (NumberFormatException nfe) {
- throw new JSONException(String.format("Value \"%s\" of attribute \"%s\" is not an integer",
- valueStr, name));
- }
+ expression.addProperty(name, element.getAttribute(name), type);
}
}
- private void copyArrayAttribute(String name, Element element, JSONObject jsonObject,
- boolean isUserAttribute) throws JSONException {
+ /** Look for @viewer, @owner within a userId attribute */
+ private void updateUserArrayState(String name, Element element) {
if (element.hasAttribute(name)) {
- StringTokenizer tokenizer = new StringTokenizer(element.getAttribute(name), ",");
- JSONArray array = new JSONArray();
- while (tokenizer.hasMoreTokens()) {
- String token = tokenizer.nextToken();
- array.put(token);
- if (isUserAttribute) {
- updateUserState(token);
- }
+ // TODO: check after Expression evaluation?
+ StringTokenizer tokens = new StringTokenizer(element.getAttribute(name), ",");
+ while (tokens.hasMoreTokens()) {
+ updateUserState(tokens.nextToken());
}
-
- jsonObject.put(name, array);
}
}
@@ -276,4 +287,187 @@
needsViewer = true;
}
}
+
+ /**
+ * A single pipelined HTTP makerequest.
+ */
+ private static class HttpData {
+ private final Expression<String> authz;
+ private final Uri base;
+ private final String href;
+ private final Expression<Boolean> signOwner;
+ private final Expression<Boolean> signViewer;
+ private final Map<String, Expression<String>> attributes;
+
+ private static final Set<String> KNOWN_ATTRIBUTES =
+ ImmutableSet.of("authz", "href", "sign_owner", "sign_viewer");
+
+ /**
+ * Create an HttpData off an <os:makeRequest> element.
+ */
+ public HttpData(Element element, Uri base) throws ElException {
+ this.base = base;
+
+ // TODO: Spec question; should authz be EL enabled?
+ String authz = element.hasAttribute("authz") ? element.getAttribute("authz") : "none";
+ this.authz = Expressions.parse(authz, String.class);
+
+ // TODO: Spec question; should EL values be URL escaped?
+ this.href = element.getAttribute("href");
+
+ // TODO: Spec question; should sign_* be EL enabled?
+ this.signOwner = booleanExpression(element, "sign_owner");
+ this.signViewer = booleanExpression(element, "sign_viewer");
+
+ Map<String, Expression<String>> attributes = Maps.newHashMap();
+ for (int i = 0; i < element.getAttributes().getLength(); i++) {
+ Node attr = element.getAttributes().item(i);
+ if (!KNOWN_ATTRIBUTES.contains(attr.getNodeName())) {
+ attributes.put(attr.getNodeName(), Expressions.parse(attr.getNodeValue(), String.class));
+ }
+ }
+
+ this.attributes = ImmutableMap.copyOf(attributes);
+ }
+
+ private HttpData(HttpData data, Substitutions substituter) {
+ this.base = data.base;
+ this.authz = data.authz;
+ this.href = substituter.substituteString(data.href);
+ this.signOwner = data.signOwner;
+ this.signViewer = data.signViewer;
+ this.attributes = data.attributes;
+ }
+
+ /** Run substitutions over an HttpData */
+ public HttpData substitute(Substitutions substituter) {
+ return new HttpData(this, substituter);
+ }
+
+ /**
+ * Evaluate expressions and return a RequestAuthenticationInfo.
+ * @throws ElException if expression evaluation fails.
+ */
+ public RequestAuthenticationInfo evaluate(ExpressionContext context) throws ElException {
+ final AuthType authType = AuthType.parse(authz.evaluate(context));
+ Expression<String> hrefExpression = Expressions.parse(href, String.class);
+ final Uri evaluatedHref = base.resolve(Uri.parse(hrefExpression.evaluate(context)));
+
+ final boolean evaluatedSignOwner = evaluateBooleanExpression(context,
+ this.signOwner, true);
+ final boolean evaluatedSignViewer = evaluateBooleanExpression(context,
+ this.signViewer, true);
+ final Map<String, String> evaluatedAttributes = Maps.newHashMap();
+ for (Map.Entry<String, Expression<String>> attr : attributes.entrySet()) {
+ evaluatedAttributes.put(attr.getKey(), attr.getValue().evaluate(context));
+ }
+
+ return new RequestAuthenticationInfo() {
+ public Map<String, String> getAttributes() {
+ return evaluatedAttributes;
+ }
+
+ public AuthType getAuthType() {
+ return authType;
+ }
+
+ public Uri getHref() {
+ return evaluatedHref;
+ }
+
+ public boolean isSignOwner() {
+ return evaluatedSignOwner;
+ }
+
+ public boolean isSignViewer() {
+ return evaluatedSignViewer;
+ }
+ };
+ }
+
+ /** Evaluate a boolean expression, choosing a default value if needed */
+ private boolean evaluateBooleanExpression(ExpressionContext context,
+ Expression<Boolean> expression, boolean defaultValue) throws ElException {
+ if (expression == null) {
+ return defaultValue;
+ }
+
+ Boolean value = expression.evaluate(context);
+ if (value == null) {
+ return defaultValue;
+ }
+
+ return value;
+ }
+
+ /** Parse a boolean expression off an XML attribute. */
+ private Expression<Boolean> booleanExpression(Element element, String attrName)
+ throws ElException {
+ if (!element.hasAttribute(attrName)) {
+ return null;
+ }
+
+ return Expressions.parse(element.getAttribute(attrName), Boolean.class);
+ }
+ }
+
+ /**
+ * A single social data request.
+ */
+ private static class SocialData {
+ private final List<Property> properties = Lists.newArrayList();
+ private final String id;
+ private final String method;
+
+ public SocialData(String id, String method) {
+ this.id = id;
+ this.method = method;
+ }
+
+ public void addProperty(String name, String value, Class<?> type) throws ElException {
+ Expression<?> expression = Expressions.parse(value, type);
+ properties.add(new Property(name, expression));
+ }
+
+ /** Create the JSON request form for the social data */
+ public JSONObject toJson(ExpressionContext context) throws ElException {
+ JSONObject object = new JSONObject();
+ try {
+ object.put("method", method);
+ object.put("id", id);
+
+ JSONObject params = new JSONObject();
+ for (Property property : properties) {
+ property.set(context, params);
+ }
+ object.put("params", params);
+ } catch (JSONException je) {
+ throw new ElException(je);
+ }
+
+ return object;
+ }
+
+ /** Single property for an expression */
+ private static class Property {
+ private final Expression<?> expression;
+ private final String name;
+
+ public Property(String name, Expression<?> expression) {
+ this.name = name;
+ this.expression = expression;
+ }
+
+ public void set(ExpressionContext context, JSONObject object) throws ElException {
+ Object value = expression.evaluate(context);
+ try {
+ if (value != null) {
+ object.put(name, value);
+ }
+ } catch (JSONException e) {
+ throw new ElException("Error parsing property \"" + name + "\"", e);
+ }
+ }
+ }
+ }
}
Added: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/expressions/ExpressionsTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/expressions/ExpressionsTest.java?rev=734296&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/expressions/ExpressionsTest.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/expressions/ExpressionsTest.java Tue Jan 13 17:41:24 2009
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+package org.apache.shindig.gadgets.expressions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.Map;
+
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+
+/**
+ * Tests of the Expressions class.
+ */
+public class ExpressionsTest {
+ private FakeContext context;
+
+ @Before
+ public void setUp() {
+ context = new FakeContext();
+ }
+
+ @Test
+ public void constantExpressions() throws Exception {
+ assertEquals("foo", Expressions.parse("foo", String.class).evaluate(context));
+ assertEquals(1, Expressions.parse("1", Integer.class).evaluate(context).intValue());
+ }
+
+ @Test
+ public void simpleExpressions() throws Exception {
+ context.variables.put("var", "value");
+ context.variables.put("int", 1);
+
+ Expression<String> var = Expressions.parse("${var}", String.class);
+ assertEquals("value", var.evaluate(context));
+
+ Expression<Integer> intExpression = Expressions.parse("${int}", Integer.class);
+ assertEquals(1, intExpression.evaluate(context).intValue());
+ }
+
+ @Test
+ public void variableNotFoundIsNull() throws Exception {
+ Expression<String> var = Expressions.parse("${var}", String.class);
+ assertNull(var.evaluate(context));
+ }
+
+ @Test
+ public void propertyEvaluationForMaps() throws Exception {
+ context.variables.put("var", ImmutableMap.of("one", 1, "two", 2));
+
+ Expression<Integer> var = Expressions.parse("${var.one}${var.two}", Integer.class);
+ // 1 and 2 concatenated make 12, not 3
+ assertEquals(12, var.evaluate(context).intValue());
+ }
+
+ @Test
+ public void propertyEvaluationForJson() throws Exception {
+ context.variables.put("var", new JSONObject("{top: {middle: {inner: 'value'}}}"));
+
+ Expression<String> var = Expressions.parse("${var.top.middle.inner}", String.class);
+ assertEquals("value", var.evaluate(context));
+ }
+
+ @Test(expected = ElException.class)
+ public void exceptionWhenCoercionFails() throws Exception {
+ context.variables.put("var", "value");
+
+ Expression<Integer> var = Expressions.parse("${var}", Integer.class);
+ var.evaluate(context);
+ }
+
+ @Test(expected = ElException.class)
+ public void exceptionIfExpressionNotClosed() throws Exception {
+ Expressions.parse("${var${foo", Integer.class);
+ }
+
+ @Test(expected = ElException.class)
+ public void exceptionWhenPropertyEvaluationFails() throws Exception {
+ Expression<String> var = Expressions.parse("${var.property}", String.class);
+ var.evaluate(context);
+ }
+
+ @Test
+ public void concatExpressions() throws Exception {
+ context.variables.put("var", "value");
+
+ Expression<String> concat = Expressions.parse("foo${var}bar", String.class);
+ assertEquals("foovaluebar", concat.evaluate(context));
+ }
+
+ static public class FakeContext implements ExpressionContext {
+ public final Map<String, Object> variables = Maps.newHashMap();
+
+ public Object getVariable(String name) {
+ return variables.get(name);
+ }
+ }
+
+}
Added: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/expressions/GadgetExpressionContextTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/expressions/GadgetExpressionContextTest.java?rev=734296&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/expressions/GadgetExpressionContextTest.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/expressions/GadgetExpressionContextTest.java Tue Jan 13 17:41:24 2009
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+package org.apache.shindig.gadgets.expressions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.UserPrefs;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Test of GadgetExpressionContext.
+ */
+public class GadgetExpressionContextTest {
+
+ private UserPrefs userPrefs;
+ private String viewParams;
+ private GadgetExpressionContext context;
+
+ @Before
+ public void setUp() throws Exception {
+ GadgetContext gadgetContext = new GadgetContext() {
+ @Override
+ public String getParameter(String name) {
+ if ("view-params".equals(name)) {
+ return viewParams;
+ }
+
+ return null;
+ }
+
+ @Override
+ public UserPrefs getUserPrefs() {
+ return userPrefs;
+ }
+ };
+
+ context = new GadgetExpressionContext(gadgetContext);
+ }
+
+ @Test
+ public void getPrefs() throws ElException {
+ userPrefs = new UserPrefs(ImmutableMap.of("foo", "bar"));
+ Expression<String> expression = Expressions.parse("${UserPrefs.foo}", String.class);
+
+ assertEquals("bar", expression.evaluate(context));
+
+ expression = Expressions.parse("${UserPrefs.wrongKey}", String.class);
+ assertNull(expression.evaluate(context));
+ }
+
+ @Test
+ public void getPrefsEmpty() throws ElException {
+ userPrefs = UserPrefs.EMPTY;
+ Expression<String> expression = Expressions.parse("${UserPrefs.foo}", String.class);
+ assertNull(expression.evaluate(context));
+ }
+
+
+ @Test
+ public void testViewParams() throws ElException {
+ viewParams = "{foo: 'bar'}";
+
+ Expression<String> expression = Expressions.parse("${ViewParams.foo}", String.class);
+ assertEquals("bar", expression.evaluate(context));
+
+ expression = Expressions.parse("${ViewParams.wrongKey}", String.class);
+ assertNull(expression.evaluate(context));
+ }
+
+ @Test
+ public void testViewParamsEmpty() throws ElException {
+ Expression<String> expression = Expressions.parse("${ViewParams.foo}", String.class);
+ assertNull(expression.evaluate(context));
+ }
+}
Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloaderTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloaderTest.java?rev=734296&r1=734295&r2=734296&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloaderTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/preload/PipelinedDataPreloaderTest.java Tue Jan 13 17:41:24 2009
@@ -47,18 +47,25 @@
public class PipelinedDataPreloaderTest extends PreloaderTestFixture {
private ContainerConfig containerConfig;
- private static final String XML = "<Module xmlns:os=\"" + PipelinedData.OPENSOCIAL_NAMESPACE + "\">"
- + "<ModulePrefs title=\"Title\"/>"
+ private static final String XML = "<Module xmlns:os=\"" + PipelinedData.OPENSOCIAL_NAMESPACE
+ + "\">" + "<ModulePrefs title=\"Title\"/>"
+ "<Content href=\"http://example.org/proxied.php\" view=\"profile\">"
+ " <os:PeopleRequest key=\"p\" userIds=\"you\"/>"
- + " <os:PersonAppDataRequest key=\"a\" userId=\"she\"/>"
- + "</Content></Module>";
+ + " <os:PersonAppDataRequest key=\"a\" userId=\"she\"/>" + "</Content></Module>";
+
+ private static final String MAKE_REQUEST_URL = "http://example.org/preload.html";
+ private static final String XML_WITH_MAKE_REQUEST = "<Module xmlns:os=\""
+ + PipelinedData.OPENSOCIAL_NAMESPACE + "\">"
+ + "<ModulePrefs title=\"Title\"/>"
+ + "<Content href=\"http://example.org/proxied.php\" view=\"profile\">"
+ + " <os:MakeRequest key=\"p\" href=\"" + MAKE_REQUEST_URL + "\" "
+ + "refreshInterval=\"60\" method=\"POST\"/>" + "</Content></Module>";
@Before
public void createContainerConfig() {
containerConfig = EasyMock.createMock(ContainerConfig.class);
- EasyMock.expect(containerConfig.get(CONTAINER, "gadgets.osDataUri"))
- .andStubReturn("http://%host%/social/rpc");
+ EasyMock.expect(containerConfig.get(CONTAINER, "gadgets.osDataUri")).andStubReturn(
+ "http://%host%/social/rpc");
EasyMock.replay(containerConfig);
}
@@ -72,8 +79,8 @@
view = "profile";
contextParams.put("st", "token");
- Collection<Callable<PreloadedData>> tasks =
- preloader.createPreloadTasks(context, spec, PreloaderService.PreloadPhase.PROXY_FETCH);
+ Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(context, spec,
+ PreloaderService.PreloadPhase.PROXY_FETCH);
assertEquals(1, tasks.size());
// Nothing fetched yet
assertEquals(0, pipeline.requests.size());
@@ -90,13 +97,43 @@
assertEquals(1, pipeline.requests.size());
HttpRequest request = pipeline.requests.get(0);
- assertEquals("http://" + context.getHost() + "/social/rpc?st=token",
- request.getUri().toString());
+ assertEquals("http://" + context.getHost() + "/social/rpc?st=token", request.getUri()
+ .toString());
assertEquals("POST", request.getMethod());
assertTrue(request.getContentType().startsWith("application/json"));
}
@Test
+ public void testHttpPreload() throws Exception {
+ GadgetSpec spec = new GadgetSpec(GADGET_URL, XML_WITH_MAKE_REQUEST);
+
+ String httpResult = "{foo: 'bar'}";
+ RecordingRequestPipeline pipeline = new RecordingRequestPipeline(httpResult);
+ PipelinedDataPreloader preloader = new PipelinedDataPreloader(pipeline, containerConfig);
+ view = "profile";
+
+ Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(context, spec,
+ PreloaderService.PreloadPhase.PROXY_FETCH);
+ assertEquals(1, tasks.size());
+ // Nothing fetched yet
+ assertEquals(0, pipeline.requests.size());
+
+ Map<String, Object> result = tasks.iterator().next().call().toJson();
+ assertEquals(1, result.size());
+
+ String expectedResult = "{data: {foo: 'bar'}, id: 'p'}";
+ assertEquals(new JSONObject(expectedResult).toString(), result.get("p").toString());
+
+ // Should have only fetched one request
+ assertEquals(1, pipeline.requests.size());
+ HttpRequest request = pipeline.requests.get(0);
+
+ assertEquals(MAKE_REQUEST_URL, request.getUri().toString());
+ assertEquals("POST", request.getMethod());
+ assertEquals(60, request.getCacheTtl());
+ }
+
+ @Test
public void testSocialPreloadForOtherView() throws Exception {
GadgetSpec spec = new GadgetSpec(GADGET_URL, XML);
@@ -106,8 +143,8 @@
view = "canvas";
contextParams.put("st", "token");
- Collection<Callable<PreloadedData>> tasks =
- preloader.createPreloadTasks(context, spec, PreloaderService.PreloadPhase.PROXY_FETCH);
+ Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(context, spec,
+ PreloaderService.PreloadPhase.PROXY_FETCH);
assertTrue(tasks.isEmpty());
}
@@ -121,8 +158,8 @@
view = "profile";
contextParams.put("st", "token");
- Collection<Callable<PreloadedData>> tasks =
- preloader.createPreloadTasks(context, spec, PreloaderService.PreloadPhase.HTML_RENDER);
+ Collection<Callable<PreloadedData>> tasks = preloader.createPreloadTasks(context, spec,
+ PreloaderService.PreloadPhase.HTML_RENDER);
assertTrue(tasks.isEmpty());
}
@@ -138,9 +175,7 @@
public HttpResponse execute(HttpRequest request) {
requests.add(request);
- return new HttpResponseBuilder()
- .setResponseString(content)
- .create();
+ return new HttpResponseBuilder().setResponseString(content).create();
}
}
}
Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/PipelinedDataTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/PipelinedDataTest.java?rev=734296&r1=734295&r2=734296&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/PipelinedDataTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/PipelinedDataTest.java Tue Jan 13 17:41:24 2009
@@ -23,13 +23,27 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.common.xml.XmlUtil;
+import org.apache.shindig.gadgets.AuthType;
+import org.apache.shindig.gadgets.GadgetContext;
+
+import java.util.Map;
+
import org.json.JSONObject;
+import org.junit.Before;
import org.junit.Test;
public class PipelinedDataTest {
//TODO: test os:MakeRequest
+ private GadgetContext context;
+
+ @Before
+ public void setUp() {
+ context = new GadgetContext();
+ }
+
@Test
public void testPeopleRequest() throws Exception {
String xml = "<Content><PeopleRequest xmlns=\"" + PipelinedData.OPENSOCIAL_NAMESPACE + "\" "
@@ -53,8 +67,35 @@
+ "fields: ['name','id']"
+ "}}");
- assertEquals(1, socialData.getSocialPreloads().size());
- assertEquals(expected.toString(), socialData.getSocialPreloads().get("key").toString());
+ assertEquals(1, socialData.getSocialPreloads(context).size());
+ assertEquals(expected.toString(), socialData.getSocialPreloads(context).get("key").toString());
+ }
+
+ @Test
+ public void testPeopleRequestWithExpressions() throws Exception {
+ String xml = "<Content><PeopleRequest xmlns=\"" + PipelinedData.OPENSOCIAL_NAMESPACE + "\" "
+ + " key=\"key\""
+ + " groupId=\"group\""
+ + " userId=\"first,second\""
+ + " startIndex=\"20\""
+ + " count=\"10\""
+ + " fields=\"name,id\""
+ + "/></Content>";
+
+ PipelinedData socialData = new PipelinedData(XmlUtil.parse(xml), null);
+ assertFalse(socialData.needsOwner());
+ assertFalse(socialData.needsViewer());
+
+ JSONObject expected = new JSONObject("{method: 'people.get', id: 'key', params:"
+ + "{groupId: 'group',"
+ + "userId: ['first','second'],"
+ + "startIndex: 20,"
+ + "count: 10,"
+ + "fields: ['name','id']"
+ + "}}");
+
+ assertEquals(1, socialData.getSocialPreloads(context).size());
+ assertEquals(expected.toString(), socialData.getSocialPreloads(context).get("key").toString());
}
@Test
@@ -73,8 +114,8 @@
+ "fields: ['name','id']"
+ "}}");
- assertEquals(1, socialData.getSocialPreloads().size());
- assertEquals(expected.toString(), socialData.getSocialPreloads().get("key").toString());
+ assertEquals(1, socialData.getSocialPreloads(context).size());
+ assertEquals(expected.toString(), socialData.getSocialPreloads(context).get("key").toString());
}
@Test
@@ -93,8 +134,8 @@
+ "fields: ['name','id']"
+ "}}");
- assertEquals(1, socialData.getSocialPreloads().size());
- assertEquals(expected.toString(), socialData.getSocialPreloads().get("key").toString());
+ assertEquals(1, socialData.getSocialPreloads(context).size());
+ assertEquals(expected.toString(), socialData.getSocialPreloads(context).get("key").toString());
}
@Test
@@ -114,8 +155,8 @@
+ "fields: ['foo','bar']"
+ "}}");
- assertEquals(1, socialData.getSocialPreloads().size());
- assertEquals(expected.toString(), socialData.getSocialPreloads().get("key").toString());
+ assertEquals(1, socialData.getSocialPreloads(context).size());
+ assertEquals(expected.toString(), socialData.getSocialPreloads(context).get("key").toString());
}
@Test
@@ -135,8 +176,8 @@
+ "fields: ['foo','bar']"
+ "}}");
- assertEquals(1, socialData.getSocialPreloads().size());
- assertEquals(expected.toString(), socialData.getSocialPreloads().get("key").toString());
+ assertEquals(1, socialData.getSocialPreloads(context).size());
+ assertEquals(expected.toString(), socialData.getSocialPreloads(context).get("key").toString());
}
@Test
@@ -150,7 +191,7 @@
PipelinedData socialData = new PipelinedData(XmlUtil.parse(xml), null);
assertFalse(socialData.needsOwner());
- assertTrue(socialData.getSocialPreloads().isEmpty());
+ assertTrue(socialData.getSocialPreloads(context).isEmpty());
}
@Test(expected = SpecParserException.class)
@@ -160,4 +201,43 @@
new PipelinedData(XmlUtil.parse(xml), null);
}
+
+ @Test
+ public void makeRequestDefaults() throws Exception {
+ String xml = "<Content><MakeRequest xmlns=\"" + PipelinedData.OPENSOCIAL_NAMESPACE + "\" "
+ + " key=\"key\""
+ + " href=\"/example.html\""
+ + "/></Content>";
+
+ PipelinedData pipelinedData = new PipelinedData(
+ XmlUtil.parse(xml), Uri.parse("http://example.org/"));
+ Map<String, RequestAuthenticationInfo> httpPreloads =
+ pipelinedData.getHttpPreloads(context);
+
+ assertEquals(1, httpPreloads.size());
+ RequestAuthenticationInfo preload = httpPreloads.get("key");
+ assertEquals(AuthType.NONE, preload.getAuthType());
+ assertEquals(Uri.parse("http://example.org/example.html"), preload.getHref());
+ }
+
+ @Test
+ public void makeRequestDefaultsSigned() throws Exception {
+ String xml = "<Content><MakeRequest xmlns=\"" + PipelinedData.OPENSOCIAL_NAMESPACE + "\" "
+ + " key=\"key\""
+ + " href=\"/example.html\""
+ + " authz=\"signed\""
+ + " sign_owner=\"false\""
+ + "/></Content>";
+
+ PipelinedData pipelinedData = new PipelinedData(
+ XmlUtil.parse(xml), Uri.parse("http://example.org/"));
+ Map<String, RequestAuthenticationInfo> httpPreloads =
+ pipelinedData.getHttpPreloads(context);
+
+ assertEquals(1, httpPreloads.size());
+ RequestAuthenticationInfo preload = httpPreloads.get("key");
+ assertEquals(AuthType.SIGNED, preload.getAuthType());
+ assertTrue(preload.isSignViewer());
+ assertFalse(preload.isSignOwner());
+ }
}
Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ViewTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ViewTest.java?rev=734296&r1=734295&r2=734296&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ViewTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/spec/ViewTest.java Tue Jan 13 17:41:24 2009
@@ -27,6 +27,7 @@
import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.common.xml.XmlUtil;
+import org.apache.shindig.gadgets.GadgetContext;
import org.apache.shindig.gadgets.variables.Substitutions;
import org.apache.shindig.gadgets.variables.Substitutions.Type;
import org.junit.Test;
@@ -207,9 +208,10 @@
+ " key=\"key\""
+ " fields=\"name,id\""
+ "/></Content>";
+ GadgetContext context = new GadgetContext();
View view = new View("test", Arrays.asList(XmlUtil.parse(xml)), SPEC_URL);
- assertEquals(1, view.getPipelinedData().getSocialPreloads().size());
- assertTrue(view.getPipelinedData().getSocialPreloads().containsKey("key"));
+ assertEquals(1, view.getPipelinedData().getSocialPreloads(context).size());
+ assertTrue(view.getPipelinedData().getSocialPreloads(context).containsKey("key"));
}
@Test(expected = SpecParserException.class)