You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by do...@apache.org on 2008/03/12 21:17:14 UTC

svn commit: r636490 - in /incubator/shindig/trunk: java/gadgets/src/main/java/org/apache/shindig/social/ java/gadgets/src/main/java/org/apache/shindig/social/opensocial/ java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/ javascript/s...

Author: doll
Date: Wed Mar 12 13:17:12 2008
New Revision: 636490

URL: http://svn.apache.org/viewvc?rev=636490&view=rev
Log:
Refactored the GadgetDataServlet to now use handlers. The sample container now uses this functionality to send requests to the server for dumping and setting state files. OpenSocial containers would use this mechanism for extending opensocial with additional data requests. In this way the main body of OpenSocial code won't have to be patched by real containers in order to be customized.

Still lots of todos to go, but the sample container is now nearly back where it was. 


Added:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/GadgetDataHandler.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/RequestItem.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/opensocial/OpenSocialDataHandler.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/StateFileDataHandler.java
Modified:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/AbstractGadgetData.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/GadgetDataServlet.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/BasicDataService.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/BasicPeopleService.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/XmlStateFileFetcher.java
    incubator/shindig/trunk/javascript/samplecontainer/samplecontainer.html

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/AbstractGadgetData.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/AbstractGadgetData.java?rev=636490&r1=636489&r2=636490&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/AbstractGadgetData.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/AbstractGadgetData.java Wed Mar 12 13:17:12 2008
@@ -118,11 +118,10 @@
 
     } else if (val instanceof Map) {
       JSONObject map = new JSONObject();
-      // TODO: Support more than just a map from String to Object
-      Map<String, Object> originalMap = (Map<String, Object>) val;
+      Map originalMap = (Map) val;
 
-      for (String item : originalMap.keySet()) {
-        map.put(item, translateObjectToJson(originalMap.get(item)));
+      for (Object item : originalMap.keySet()) {
+        map.put(item.toString(), translateObjectToJson(originalMap.get(item)));
       }
       return map;
 

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/GadgetDataHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/GadgetDataHandler.java?rev=636490&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/GadgetDataHandler.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/GadgetDataHandler.java Wed Mar 12 13:17:12 2008
@@ -0,0 +1,37 @@
+/*
+ * 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.social;
+
+public interface GadgetDataHandler {
+  /**
+   * Determines whether this handler should be used to process the request
+   *
+   * @param requestType The type of request made
+   * @return true if this handler should be called to create a response item for
+   *     this json request
+   */
+  boolean shouldHandle(String requestType);
+
+  /**
+   * Constructs a ResponseItem based on the parameters in the RequestItem
+   *
+   * @param request The request from the json
+   * @return The corresponding response item
+   */
+  ResponseItem handleRequest(RequestItem request);
+}

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/GadgetDataServlet.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/GadgetDataServlet.java?rev=636490&r1=636489&r2=636490&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/GadgetDataServlet.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/GadgetDataServlet.java Wed Mar 12 13:17:12 2008
@@ -22,8 +22,10 @@
 import org.json.JSONObject;
 import org.apache.shindig.social.samplecontainer.BasicPeopleService;
 import org.apache.shindig.social.samplecontainer.BasicDataService;
+import org.apache.shindig.social.samplecontainer.StateFileDataHandler;
 import org.apache.shindig.social.opensocial.PeopleService;
 import org.apache.shindig.social.opensocial.DataService;
+import org.apache.shindig.social.opensocial.OpenSocialDataHandler;
 import org.apache.shindig.social.opensocial.model.IdSpec;
 import org.apache.shindig.social.opensocial.model.OpenSocialDataType;
 
@@ -38,24 +40,43 @@
 import javax.servlet.http.HttpServletResponse;
 
 /**
- * Servlet for serving social data. This is a very basic hardcoded inital file.
- * This will expand to be more sophisticated as time goes on.
+ * Servlet for handling gadget requests for data. The request accepts one json
+ * parameter of the format:
+ *
+ * request = [{type: <string>, other parameters}, ...]
+ *
+ * This gets mapped to a list of RequestItems. These items will be passed to one
+ * of the registered handlers, which in turn produce ResponseItems. If no
+ * handler is found a NOT_IMPLEMENTED ResponseItem will be created.
+ *
+ * This list of ResponseItems will get passed back to the gadget with this form:
+ *
+ * responses = [{response: <any json string>, error: <ResponseError>}]
+ *
+ * This class is meant to work with the logic in jsoncontainer.js.
  */
 public class GadgetDataServlet extends HttpServlet {
   private static final Logger logger
       = Logger.getLogger("org.apache.shindig.social");
 
   // TODO: get through injection
-  private PeopleService peopleHandler = new BasicPeopleService();
-  private DataService dataHandler = new BasicDataService();
+  private static List<GadgetDataHandler> handlers
+      = new ArrayList<GadgetDataHandler>();
+
+  static {
+    // TODO: Should we just use a map from String type to Handler?
+    handlers.add(new OpenSocialDataHandler());
+    handlers.add(new StateFileDataHandler());
+  }
 
   @Override
   protected void doPost(HttpServletRequest req, HttpServletResponse resp)
       throws IOException {
-    // TODO: Get the security token
-    // TODO: etc, etc, etc
+    // TODO: Get the security token and pass the viewer and owner along in the
+    // request items 
 
     String requestParam = req.getParameter("request");
+
     DataResponse response;
     try {
       response = new DataResponse(createResponse(requestParam));
@@ -71,48 +92,23 @@
       throws JSONException {
     // TODO: Improve json input handling. The json request should get auto
     // translated into objects
-    JSONArray requestItems = new JSONArray(requestParam);
     List<ResponseItem> responseItems = new ArrayList<ResponseItem>();
+
+    JSONArray requestItems = new JSONArray(requestParam);
     int length = requestItems.length();
 
     for (int i = 0; i < length; i++) {
       ResponseItem response = new ResponseItem<Object>(
           ResponseError.NOT_IMPLEMENTED);
 
-      JSONObject requestItem = requestItems.getJSONObject(i);
-
-      try {
-        OpenSocialDataType type = OpenSocialDataType.valueOf(
-            requestItem.getString("type"));
-
-        String jsonSpec = requestItem.getString("idSpec");
-        List<String> peopleIds = peopleHandler.getIds(IdSpec.fromJson(jsonSpec));
-
-        // TODO: Abstract this logic into handlers which register
-        switch (type) {
-          case FETCH_PEOPLE :
-            response = peopleHandler.getPeople(peopleIds);
-            break;
-
-          case FETCH_PERSON_APP_DATA :
-            response = dataHandler.getPersonData(peopleIds);
-            break;
-
-          case UPDATE_PERSON_APP_DATA:
-            // We only support updating one person right now
-            String id = peopleIds.get(0);
-
-            String key = requestItem.getString("key");
-            String value = requestItem.getString("value");
-
-            response = dataHandler.updatePersonData(id, key, value);
-            break;
+      JSONObject jsonRequest = requestItems.getJSONObject(i);
+      RequestItem requestItem = new RequestItem(jsonRequest.getString("type"),
+          jsonRequest);
+
+      for (GadgetDataHandler handler : handlers) {
+        if (handler.shouldHandle(requestItem.getType())) {
+          response = handler.handleRequest(requestItem);
         }
-
-      } catch (JSONException e) {
-        response = new ResponseItem<Object>(ResponseError.BAD_REQUEST);
-      } catch (IllegalArgumentException e) {
-        response = new ResponseItem<Object>(ResponseError.BAD_REQUEST);
       }
 
       responseItems.add(response);

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/RequestItem.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/RequestItem.java?rev=636490&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/RequestItem.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/RequestItem.java Wed Mar 12 13:17:12 2008
@@ -0,0 +1,50 @@
+/*
+ * 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.social;
+
+import org.json.JSONObject;
+
+/**
+ * Represents the request items that come from the json. Each RequestItem should
+ * map to one ResponseItem.
+ */
+public class RequestItem extends AbstractGadgetData {
+  private String type;
+  private JSONObject params;
+
+  public RequestItem(String type, JSONObject params) {
+    this.type = type;
+    this.params = params;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public JSONObject getParams() {
+    return params;
+  }
+
+  public void setParams(JSONObject params) {
+    this.params = params;
+  }
+}
\ No newline at end of file

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/opensocial/OpenSocialDataHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/opensocial/OpenSocialDataHandler.java?rev=636490&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/opensocial/OpenSocialDataHandler.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/opensocial/OpenSocialDataHandler.java Wed Mar 12 13:17:12 2008
@@ -0,0 +1,100 @@
+/*
+ * 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.social.opensocial;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.apache.shindig.social.samplecontainer.BasicPeopleService;
+import org.apache.shindig.social.samplecontainer.BasicDataService;
+import org.apache.shindig.social.opensocial.PeopleService;
+import org.apache.shindig.social.opensocial.DataService;
+import org.apache.shindig.social.opensocial.model.IdSpec;
+import org.apache.shindig.social.opensocial.model.OpenSocialDataType;
+import org.apache.shindig.social.*;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.logging.Logger;
+import java.util.List;
+import java.util.ArrayList;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Servlet for serving the data required for opensocial.
+ * This will expand to be more sophisticated as time goes on.
+ */
+public class OpenSocialDataHandler implements GadgetDataHandler {
+  private static final Logger logger
+      = Logger.getLogger("org.apache.shindig.social");
+
+  // TODO: get through injection
+  private static PeopleService peopleHandler = new BasicPeopleService();
+  private static DataService dataHandler = new BasicDataService();
+
+  public boolean shouldHandle(String requestType) {
+    try {
+      // There should be a cleaner way to do this...
+      OpenSocialDataType.valueOf(requestType);
+      return true;
+    } catch (IllegalArgumentException e) {
+      return false;
+    }
+  }
+
+  public ResponseItem handleRequest(RequestItem request) {
+    OpenSocialDataType type = OpenSocialDataType.valueOf(request.getType());
+    ResponseItem response = new ResponseItem<Object>(
+        ResponseError.NOT_IMPLEMENTED);
+
+    try {
+      String jsonSpec = request.getParams().getString("idSpec");
+      List<String> peopleIds = peopleHandler.getIds(IdSpec.fromJson(jsonSpec));
+
+      switch (type) {
+        case FETCH_PEOPLE :
+          response = peopleHandler.getPeople(peopleIds);
+          break;
+
+        case FETCH_PERSON_APP_DATA :
+          response = dataHandler.getPersonData(peopleIds);
+          break;
+
+        case UPDATE_PERSON_APP_DATA:
+          // We only support updating one person right now
+          String id = peopleIds.get(0);
+
+          String key = request.getParams().getString("key");
+          String value = request.getParams().getString("value");
+
+          response = dataHandler.updatePersonData(id, key, value);
+          break;
+      }
+
+    } catch (JSONException e) {
+      response = new ResponseItem<Object>(ResponseError.BAD_REQUEST);
+    } catch (IllegalArgumentException e) {
+      response = new ResponseItem<Object>(ResponseError.BAD_REQUEST);
+    }
+
+    return response;
+  }
+}
\ No newline at end of file

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/BasicDataService.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/BasicDataService.java?rev=636490&r1=636489&r2=636490&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/BasicDataService.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/BasicDataService.java Wed Mar 12 13:17:12 2008
@@ -32,52 +32,13 @@
 import java.util.Map;
 
 public class BasicDataService implements DataService {
-  // TODO: This obviously won't work on multiple servers
-  // If we care then we should do something about it
-  private static Map<String, Map<String, String>> allData;
-
-  public BasicDataService() {
-    if (allData != null) {
-      return;
-    }
-
-    allData = new HashMap<String, Map<String, String>>();
-
-    // TODO: Should use guice here for one global thingy and get url from web ui
-    String stateFile = "http://localhost:8080/gadgets/files/samplecontainer/state-basicfriendlist.xml";
-    XmlStateFileFetcher fetcher = new XmlStateFileFetcher(stateFile);
-    Document doc = fetcher.fetchStateDocument(true);
-    setupData(doc);
-  }
-
-  private void setupData(Document stateDocument) {
-    Element root = stateDocument.getDocumentElement();
-
-    NodeList elements = root.getElementsByTagName("personAppData");
-    NodeList personDataNodes = elements.item(0).getChildNodes();
-
-    for (int i = 0; i < personDataNodes.getLength(); i++) {
-      Node personDataNode = personDataNodes.item(i);
-      NamedNodeMap attributes = personDataNode.getAttributes();
-      if (attributes == null) {
-        continue;
-      }
-
-      String id = attributes.getNamedItem("person").getNodeValue();
-      String field = attributes.getNamedItem("field").getNodeValue();
-      String value = personDataNode.getTextContent();
-
-      Map<String, String> currentData = allData.get(id);
-      if (currentData == null) {
-        currentData = new HashMap<String, String>();
-        allData.put(id, currentData);
-      }
-      currentData.put(field, value);
-    }
-  }
 
   public ResponseItem<Map<String, Map<String, String>>> getPersonData(
       List<String> ids) {
+
+    Map<String, Map<String, String>> allData
+        = XmlStateFileFetcher.get().getAppData();
+
     // TODO: Use the opensource Collections library
     Map<String, Map<String, String>> data =
         new HashMap<String, Map<String, String>>();
@@ -94,13 +55,7 @@
       return new ResponseItem<Object>(ResponseError.BAD_REQUEST, null);
     }
 
-    Map<String, String> personData = allData.get(id);
-    if (personData == null) {
-      personData = new HashMap<String, String>();
-      allData.put(id, personData);
-    }
-
-    personData.put(key, value);
+    XmlStateFileFetcher.get().setAppData(id, key, value);
     return new ResponseItem<JSONObject>(new JSONObject());
   }
 

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/BasicPeopleService.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/BasicPeopleService.java?rev=636490&r1=636489&r2=636490&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/BasicPeopleService.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/BasicPeopleService.java Wed Mar 12 13:17:12 2008
@@ -36,80 +36,10 @@
 import java.util.Map;
 
 public class BasicPeopleService implements PeopleService {
-  private Map<IdSpec.Type, List<String>> idMap
-      = new HashMap<IdSpec.Type, List<String>>();
-  private Map<String, Person> allPeople
-      = new HashMap<String, Person>();
-
-  public BasicPeopleService() {
-    // TODO: Get file from user in web ui
-    String stateFile = "http://localhost:8080/gadgets/files/samplecontainer/state-basicfriendlist.xml";
-    XmlStateFileFetcher fetcher = new XmlStateFileFetcher(stateFile);
-    Document doc = fetcher.fetchStateDocument(true);
-    setupData(doc);
-  }
-
-  private void setupData(Document stateDocument) {
-    Element root = stateDocument.getDocumentElement();
-
-    // TODO: Eventually the viewer and owner shouldn't be hardcoded. You should
-    // be able to visit other allPeople's "profile" pages in the sample container
-    setupPeopleInXmlTag(root, "viewer", IdSpec.Type.VIEWER);
-    setupPeopleInXmlTag(root, "owner", IdSpec.Type.OWNER);
-    setupPeopleInXmlTag(root, "viewerFriends", IdSpec.Type.VIEWER_FRIENDS);
-    setupPeopleInXmlTag(root, "ownerFriends", IdSpec.Type.OWNER_FRIENDS);
-
-    // Handle empty people
-    if (idMap.get(IdSpec.Type.OWNER).isEmpty()) {
-      idMap.put(IdSpec.Type.OWNER, idMap.get(IdSpec.Type.VIEWER));
-    }
-
-    if (idMap.get(IdSpec.Type.OWNER_FRIENDS).isEmpty()) {
-      idMap.put(IdSpec.Type.OWNER_FRIENDS,
-          idMap.get(IdSpec.Type.VIEWER_FRIENDS));
-    }
-  }
-
-  // Adds all people in the xml tag to the allPeople map.
-  // Also returns the relevant ids
-  private void setupPeopleInXmlTag(Element root, String tagName,
-      IdSpec.Type idType) {
-    // TODO: Use the opensource Collections library
-    List<String> ids = new ArrayList<String>();
-
-    NodeList elements = root.getElementsByTagName(tagName);
-    if (elements == null || elements.item(0) == null) {
-      idMap.put(idType, ids);
-      return;
-    }
-
-    NodeList personNodes = elements.item(0).getChildNodes();
-
-    for (int i = 0; i < personNodes.getLength(); i++) {
-      NamedNodeMap attributes = personNodes.item(i).getAttributes();
-      if (attributes == null) {
-        continue;
-      }
-
-      String name = attributes.getNamedItem("name").getNodeValue();
-      String id = attributes.getNamedItem("id").getNodeValue();
-      Person person = new Person(id, new Name(name));
-
-      Node phoneItem = attributes.getNamedItem("phone");
-      if (phoneItem != null) {
-        String phone = phoneItem.getNodeValue();
-        Phone[] phones = {new Phone(phone, null)};
-        person.setPhoneNumbers(phones);
-      }
-
-      allPeople.put(id, person);
-      ids.add(id);
-    }
-
-    idMap.put(idType, ids);
-  }
 
   public ResponseItem<List<Person>> getPeople(List<String> ids) {
+    Map<String, Person> allPeople = XmlStateFileFetcher.get().getAllPeople();
+
     List<Person> people = new ArrayList<Person>();
     for (String id : ids) {
       people.add(allPeople.get(id));
@@ -118,6 +48,9 @@
   }
 
   public List<String> getIds(IdSpec idSpec) throws JSONException {
+    Map<IdSpec.Type, List<String>> idMap
+        = XmlStateFileFetcher.get().getIdMap();
+
     if (idSpec.getType() == IdSpec.Type.USER_IDS) {
       return idSpec.fetchUserIds();
     } else {

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/StateFileDataHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/StateFileDataHandler.java?rev=636490&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/StateFileDataHandler.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/StateFileDataHandler.java Wed Mar 12 13:17:12 2008
@@ -0,0 +1,83 @@
+/*
+ * 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.social.samplecontainer;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.apache.shindig.social.ResponseItem;
+import org.apache.shindig.social.ResponseError;
+import org.apache.shindig.social.GadgetDataHandler;
+import org.apache.shindig.social.RequestItem;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Servlet for serving the data required for opensocial.
+ * This will expand to be more sophisticated as time goes on.
+ */
+public class StateFileDataHandler implements GadgetDataHandler {
+
+  public enum RequestType {
+    DUMP_STATE, SET_STATE
+  }
+
+  public boolean shouldHandle(String requestType) {
+     try {
+      // There should be a cleaner way to do this...
+      RequestType.valueOf(requestType);                                         
+      return true;
+    } catch (IllegalArgumentException e) {
+      return false;
+    }
+  }
+
+  public ResponseItem handleRequest(RequestItem request) {
+    RequestType type = RequestType.valueOf(request.getType());
+    ResponseItem response = new ResponseItem<Object>(
+        ResponseError.NOT_IMPLEMENTED);
+
+    XmlStateFileFetcher fetcher = XmlStateFileFetcher.get();
+
+    switch (type) {
+      case DUMP_STATE:
+        Map<String, Object> state = new HashMap<String, Object>();
+        state.put("people", fetcher.getAllPeople());
+        state.put("idMap", fetcher.getIdMap());
+        state.put("data", fetcher.getAppData());
+        response = new ResponseItem<Map<String, Object>>(state);
+        break;
+      case SET_STATE:
+        try {
+          String stateFile = request.getParams().getString("fileUrl");
+          fetcher.resetStateFile(new URI(stateFile));
+          response = new ResponseItem<Object>(new JSONObject());
+        } catch (URISyntaxException e) {
+          response = new ResponseItem<Object>(ResponseError.BAD_REQUEST);
+        } catch (JSONException e) {
+          response = new ResponseItem<Object>(ResponseError.BAD_REQUEST);
+        }
+        break;
+
+    }
+
+    return response;
+  }
+}
\ No newline at end of file

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/XmlStateFileFetcher.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/XmlStateFileFetcher.java?rev=636490&r1=636489&r2=636490&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/XmlStateFileFetcher.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/social/samplecontainer/XmlStateFileFetcher.java Wed Mar 12 13:17:12 2008
@@ -1,10 +1,14 @@
 package org.apache.shindig.social.samplecontainer;
 
-import org.w3c.dom.Document;
+import org.w3c.dom.*;
 import org.apache.shindig.gadgets.RemoteContentFetcher;
 import org.apache.shindig.gadgets.BasicRemoteContentFetcher;
 import org.apache.shindig.gadgets.RemoteContent;
 import org.apache.shindig.gadgets.RemoteContentRequest;
+import org.apache.shindig.social.opensocial.model.IdSpec;
+import org.apache.shindig.social.opensocial.model.Person;
+import org.apache.shindig.social.opensocial.model.Name;
+import org.apache.shindig.social.opensocial.model.Phone;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 
@@ -14,37 +18,56 @@
 import java.net.URISyntaxException;
 import java.io.StringReader;
 import java.io.IOException;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
 
 /**
  * @author Cassandra Doll <do...@google.com>
  */
 public class XmlStateFileFetcher {
-  private String stateFile;
-  private Document document;
+  private static String defaultStateUrl =
+      "http://localhost:8080/gadgets/files/samplecontainer/state-basicfriendlist.xml";
 
-  // TODO: Prob change to a Uri param, let the stateFile setter deal
-  // with the exception.
-  public XmlStateFileFetcher(String stateFile) {
-    this.stateFile = stateFile;
-  }
+  // TODO: Should use guice here This static fetcher is very gross.
+  private static XmlStateFileFetcher fetcher;
 
-  public Document fetchStateDocument(boolean useCache) {
-    if (useCache && document != null) {
-      return document;
+  public static XmlStateFileFetcher get() {
+    if (fetcher == null) {
+      fetcher = new XmlStateFileFetcher();
     }
+    return fetcher;
+  }
 
-    URI uri;
-    try {
-      uri = new URI(stateFile);
+
+  private URI stateFile;
+  private Document document;
+
+  // TODO: This obviously won't work on multiple servers
+  // If we care then we should do something about it
+  private Map<String, Map<String, String>> allData;
+  private Map<IdSpec.Type, List<String>> idMap;
+  private Map<String, Person> allPeople;
+
+  private XmlStateFileFetcher() {
+   try {
+      this.stateFile = new URI(defaultStateUrl);
     } catch (URISyntaxException e) {
-      throw new RuntimeException("The state file " + stateFile
-          + " does not point to a valid uri", e);
+      throw new RuntimeException(
+          "The default state file could not be fetched. ", e);
+    }
+  }
+
+  private Document fetchStateDocument() {
+    if (document != null) {
+      return document;
     }
 
     // TODO: Eventually get the fetcher and processing options from a
     // config file, just like the GadgetServer
     RemoteContentFetcher fetcher = new BasicRemoteContentFetcher(1024 * 1024);
-    RemoteContent xml = fetcher.fetch(new RemoteContentRequest(uri));
+    RemoteContent xml = fetcher.fetch(new RemoteContentRequest(stateFile));
 
     InputSource is = new InputSource(new StringReader(
         xml.getResponseAsString()));
@@ -63,4 +86,139 @@
       throw new RuntimeException(errorMessage, e);
     }
   }
+
+  public Map<String, Map<String, String>> getAppData() {
+    if (allData != null) {
+      return allData;
+    }
+
+    allData = new HashMap<String, Map<String, String>>();
+
+    Element root = this.fetchStateDocument().getDocumentElement();
+
+    NodeList elements = root.getElementsByTagName("personAppData");
+    NodeList personDataNodes = elements.item(0).getChildNodes();
+
+    for (int i = 0; i < personDataNodes.getLength(); i++) {
+      Node personDataNode = personDataNodes.item(i);
+      NamedNodeMap attributes = personDataNode.getAttributes();
+      if (attributes == null) {
+        continue;
+      }
+
+      String id = attributes.getNamedItem("person").getNodeValue();
+      String field = attributes.getNamedItem("field").getNodeValue();
+      String value = personDataNode.getTextContent();
+
+      Map<String, String> currentData = allData.get(id);
+      if (currentData == null) {
+        currentData = new HashMap<String, String>();
+        allData.put(id, currentData);
+      }
+      currentData.put(field, value);
+    }
+
+    return allData;
+  }
+
+  public void setAppData(String id, String key, String value) {
+    if (allData == null) {
+      setupPeopleData();
+    }
+
+    Map<String, String> personData = allData.get(id);
+    if (personData == null) {
+      personData = new HashMap<String, String>();
+      allData.put(id, personData);
+    }
+
+    personData.put(key, value);
+  }
+
+  public Map<IdSpec.Type, List<String>> getIdMap() {
+    if (idMap == null) {
+      setupPeopleData();
+    }
+    return idMap;
+  }
+
+  public Map<String, Person> getAllPeople() {
+    if (allPeople == null) {
+      setupPeopleData();
+    }
+    return allPeople;
+  }
+
+  private void setupPeopleData() {
+    Element root = this.fetchStateDocument().getDocumentElement();
+
+    idMap = new HashMap<IdSpec.Type, List<String>>();
+    allPeople = new HashMap<String, Person>();
+
+    // TODO: Eventually the viewer and owner shouldn't be hardcoded. You should
+    // be able to visit other allPeople's "profile" pages in the sample container
+    setupPeopleInXmlTag(root, "viewer", IdSpec.Type.VIEWER);
+    setupPeopleInXmlTag(root, "owner", IdSpec.Type.OWNER);
+    setupPeopleInXmlTag(root, "viewerFriends", IdSpec.Type.VIEWER_FRIENDS);
+    setupPeopleInXmlTag(root, "ownerFriends", IdSpec.Type.OWNER_FRIENDS);
+
+    // Handle empty people
+    if (idMap.get(IdSpec.Type.OWNER).isEmpty()) {
+      idMap.put(IdSpec.Type.OWNER, idMap.get(IdSpec.Type.VIEWER));
+    }
+
+    if (idMap.get(IdSpec.Type.OWNER_FRIENDS).isEmpty()) {
+      idMap.put(IdSpec.Type.OWNER_FRIENDS,
+          idMap.get(IdSpec.Type.VIEWER_FRIENDS));
+    }
+  }
+
+  // Adds all people in the xml tag to the allPeople map.
+  // Also puts ids into the idMap under the idType key
+  private void setupPeopleInXmlTag(Element root, String tagName,
+      IdSpec.Type idType) {
+    // TODO: Use the opensource Collections library
+    List<String> ids = new ArrayList<String>();
+
+    NodeList elements = root.getElementsByTagName(tagName);
+    if (elements == null || elements.item(0) == null) {
+      idMap.put(idType, ids);
+      return;
+    }
+
+    NodeList personNodes = elements.item(0).getChildNodes();
+
+    for (int i = 0; i < personNodes.getLength(); i++) {
+      NamedNodeMap attributes = personNodes.item(i).getAttributes();
+      if (attributes == null) {
+        continue;
+      }
+
+      String name = attributes.getNamedItem("name").getNodeValue();
+      String id = attributes.getNamedItem("id").getNodeValue();
+      Person person = new Person(id, new Name(name));
+
+      Node phoneItem = attributes.getNamedItem("phone");
+      if (phoneItem != null) {
+        String phone = phoneItem.getNodeValue();
+        Phone[] phones = {new Phone(phone, null)};
+        person.setPhoneNumbers(phones);
+      }
+
+      allPeople.put(id, person);
+      ids.add(id);
+    }
+
+    idMap.put(idType, ids);
+  }
+
+
+  public void resetStateFile(URI stateFile) {
+    this.stateFile = stateFile;
+    this.document = null;
+    this.allData = null;
+    this.idMap = null;
+    this.allPeople = null;
+  }
+
 }

Modified: incubator/shindig/trunk/javascript/samplecontainer/samplecontainer.html
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/samplecontainer/samplecontainer.html?rev=636490&r1=636489&r2=636490&view=diff
==============================================================================
--- incubator/shindig/trunk/javascript/samplecontainer/samplecontainer.html (original)
+++ incubator/shindig/trunk/javascript/samplecontainer/samplecontainer.html Wed Mar 12 13:17:12 2008
@@ -30,6 +30,10 @@
 var parentUrl = document.location.href;
 var baseUrl = parentUrl.substring(0, parentUrl.indexOf('samplecontainer.html'))
 
+// TODO: This is gross, it needs to use the config just like the gadget js does
+var socialDataPath = document.location.protocol + "//" + document.location.host
+    + "/gadgets/socialdata";
+
 var gadgetUrl = baseUrl + 'examples/SocialHelloWorld.xml';
 var gadgetUrlCookie = 'sampleContainerGadgetUrl';
 
@@ -54,11 +58,11 @@
   var cookieStateFileUrl = decodeURIComponent(goog.net.cookies.get(stateFileUrlCookie));
   if (cookieStateFileUrl && cookieStateFileUrl != "undefined") {
     stateFileUrl = cookieStateFileUrl;
+    reloadStateFile();
   }
 
-  // Pass state file to server
+  // Setup state file
   document.getElementById("stateFileUrl").value = stateFileUrl;
-  // TODO: Pass the state file to the server
 
   // Render gadget
   document.getElementById("gadgetUrl").value = gadgetUrl;
@@ -80,7 +84,7 @@
   doEvil = document.getElementById("doEvilCheckbox").checked;
 
   stateFileUrl = document.getElementById("stateFileUrl").value;
-  // TODO: reset the state file
+  reloadStateFile();
   goog.net.cookies.set(stateFileUrlCookie, encodeURIComponent(stateFileUrl));
 
   gadgetUrl = document.getElementById("gadgetUrl").value;
@@ -110,6 +114,120 @@
 };
 gadgets.container.gadgetClass = SampleContainerGadget;
 
+function reloadStateFile() {
+  // TODO: Should re-use the jsoncontainer code somehow
+  var jsonRequest = gadgets.json.stringify(
+      [{"type" : "SET_STATE", "fileUrl" : stateFileUrl}]);
+
+  var makeRequestParams = {
+    "CONTENT_TYPE" : "JSON",
+    "METHOD" : "POST",
+    "AUTHORIZATION" : "SIGNED",
+    "POST_DATA" : encodeValues({'request' : jsonRequest})};
+
+  makeRequest(socialDataPath,
+      function(data) {
+        data = data.data;
+        if (!data || data['error'] ||
+            !data['responses'][0] || data['responses'][0]['error']) {
+          alert("The state file you requested could not be loaded");
+        }
+
+      },
+      makeRequestParams);
+};
+
+function dumpStateFile() {
+  // TODO: Should re-use the jsoncontainer code somehow
+  var jsonRequest = gadgets.json.stringify(
+      [{"type" : "DUMP_STATE"}]);
+
+  var makeRequestParams = {
+    "CONTENT_TYPE" : "JSON",
+    "METHOD" : "POST",
+    "AUTHORIZATION" : "SIGNED",
+    "POST_DATA" : encodeValues({'request' : jsonRequest})};
+
+  makeRequest(socialDataPath,
+      function(data) {
+        data = data.data;
+        if (!data || data['error'] ||
+            !data['responses'][0] || data['responses'][0]['error']) {
+          alert("The state file could not be dumped");
+        } else {
+          document.getElementById('gadgetState').innerHTML
+              = gadgets.json.stringify(data['responses'][0]['response']);
+        }
+
+      },
+      makeRequestParams);
+
+};
+
+
+// Xhr stuff that is copied from io.js.
+// TODO: We should really get rid of the duplication
+function makeXhr() {
+  if (window.XMLHttpRequest) {
+    return new XMLHttpRequest();
+  } else if (window.ActiveXObject) {
+    var x = new ActiveXObject("Msxml2.XMLHTTP");
+    if (!x) {
+      x = new ActiveXObject("Microsoft.XMLHTTP");
+    }
+    return x;
+  }
+};
+
+function processResponse(url, callback, params, xobj) {
+  if (xobj.readyState !== 4) {
+    return;
+  }
+  if (xobj.status !== 200) {
+    // TODO Need to work on standardizing errors
+    callback({errors : ["Error " + xobj.status] });
+    return;
+  }
+  var txt = xobj.responseText;
+
+    // We are using eval directly here because the outer response comes from a
+  // trusted source, and json parsing is slow in IE.
+  var data = eval("(" + txt + ")");
+  var resp = {
+    data: data
+  };
+
+  callback(resp);
+};
+
+function makeRequest(url, callback, params) {
+  var xhr = makeXhr();
+  xhr.open("POST", url, true);
+  xhr.onreadystatechange = gadgets.util.makeClosure(
+      null, processResponse, url, callback, params, xhr);
+  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+  xhr.send(params.POST_DATA);
+};
+
+function encodeValues(fields, opt_noEscaping) {
+  var escape = !opt_noEscaping;
+
+  var buf = [];
+  var first = false;
+  for (var i in fields) if (fields.hasOwnProperty(i)) {
+    if (!first) {
+      first = true;
+    } else {
+      buf.push("&");
+    }
+    buf.push(escape ? encodeURIComponent(i) : i);
+    buf.push("=");
+    buf.push(escape ? encodeURIComponent(fields[i]) : fields[i]);
+  }
+  return buf.join("");
+};
+
+
 </script>
 </head>
 <body onLoad="initGadget();">
@@ -129,10 +247,12 @@
       <br/>
 
       <input type="button" value="reset all" onclick="changeGadgetUrl();"/>
+      <input type="button" value="dump state" onclick="dumpStateFile();"/>
     </div>
     <div style="clear:both; height: 1px;">&nbsp;</div>
   </div>
 
+  <div id="gadgetState" style="font-size:smaller"></div>
   <div id="gadget-chrome" class="gadgets-gadget-chrome" style="width:80%;"></div>
 </body>
 </html>