Author: chabotc
Date: Thu Aug 28 05:17:19 2008
New Revision: 689797
URL: http://svn.apache.org/viewvc?rev=689797&view=rev
Log:
SHINDIG-539 by Ram Sharma, Adds XML universal input and output format support
for the RESTful interface
Added:
incubator/shindig/trunk/php/src/social-api/converters/InputXmlConverter.php
incubator/shindig/trunk/php/src/social-api/converters/OutputXmlConverter.php
Modified:
incubator/shindig/trunk/php/src/social-api/http/RestServlet.php
Added:
incubator/shindig/trunk/php/src/social-api/converters/InputXmlConverter.php
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/converters/InputXmlConverter.php?rev=689797&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/social-api/converters/InputXmlConverter.php
(added)
+++ incubator/shindig/trunk/php/src/social-api/converters/InputXmlConverter.php
Thu Aug 28 05:17:19 2008
@@ -0,0 +1,96 @@
+<?php
+/*
+ * 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.
+ */
+
+/**
+ * Convert Xml representations to the internal data structure representation
+ */
+class InputXmlConverter extends InputConverter {
+
+ public function convertPeople($requestParam)
+ {
+ throw new Exception("Operation not supported");
+ }
+
+ public function convertActivities($requestParam)
+ {
+ $activity = array();
+ $xml = simplexml_load_string($requestParam, 'SimpleXMLElement',
LIBXML_NOCDATA);
+ if (! isset($xml->title)) {
+ throw new Exception("Mallformed activity xml");
+ }
+ // remember to either type cast to (string) or trim() the
string so we don't get
+ // SimpleXMLString types in the internal data representation. I
often prefer
+ // using trim() since it cleans up the data too
+ $activity['id'] = isset($xml->id) ? trim($xml->id) : '';
+ $activity['title'] = trim($xml->title);
+ $activity['body'] = isset($xml->summary) ? trim($xml->summary)
: '';
+ $activity['streamTitle'] = isset($xml->activity->streamTitle) ?
trim($xml->activity->streamTitle) : '';
+ $activity['streamId'] = isset($xml->activity->streamId) ?
trim($xml->activity->streamId) : '';
+ $activity['updated'] = isset($xml->updated) ?
trim($xml->updated) : '';
+ if (isset($xml->activity->mediaItems)) {
+ $activity['mediaItems'] = array();
+ foreach ($xml->activity->mediaItems->MediaItem as
$mediaItem) {
+ $item = array();
+ if (! isset($mediaItem->type) || !
isset($mediaItem->mimeType) || ! isset($mediaItem->url)) {
+ throw new Exception("Invalid media item
in activity xml");
+ }
+ $item['type'] = trim($mediaItem->type);
+ $item['mimeType'] = trim($mediaItem->mimeType);
+ $item['url'] = trim($mediaItem->url);
+ $activity['mediaItems'][] = $item;
+ }
+ }
+ return $activity;
+ }
+
+ public function convertAppData($requestParam)
+ {
+ $xml = simplexml_load_string($requestParam, 'SimpleXMLElement',
LIBXML_NOCDATA);
+ if (! isset($xml->appdata)) {
+ throw new Exception("Mallformed AppData xml");
+ }
+ $data = array();
+ foreach (get_object_vars($xml->appdata) as $key => $val) {
+ $data[trim($key)] = trim($val);
+ }
+ return $data;
+ }
+
+ public function convertMessages($requestParam)
+ {
+ $xml = simplexml_load_string($requestParam, 'SimpleXMLElement',
LIBXML_NOCDATA);
+ $message = array();
+ if (! isset($xml->title) || ! isset($xml->body)) {
+ throw new Exception("Invalid message structure");
+ }
+ $message['id'] = isset($xml->id) ? trim($xml->id) : null;
+ $message['title'] = trim($xml->title);
+ $message['body'] = trim($xml->body);
+ // retrieve recipients by looking at the osapi name space
+ $xml = simplexml_load_string($requestParam, 'SimpleXMLElement',
LIBXML_NOCDATA, "http://opensocial.org/2008/opensocialapi");
+ if (! isset($xml->recipient)) {
+ throw new Exception("Invalid message structure");
+ }
+ $message['recipients'] = array();
+ foreach ($xml->recipient as $recipient) {
+ $message['recipients'][] = trim($recipient);
+ }
+ return $message;
+ }
+}
Added:
incubator/shindig/trunk/php/src/social-api/converters/OutputXmlConverter.php
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/converters/OutputXmlConverter.php?rev=689797&view=auto
==============================================================================
---
incubator/shindig/trunk/php/src/social-api/converters/OutputXmlConverter.php
(added)
+++
incubator/shindig/trunk/php/src/social-api/converters/OutputXmlConverter.php
Thu Aug 28 05:17:19 2008
@@ -0,0 +1,195 @@
+<?php
+/*
+ * 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.
+ */
+
+/**
+ * Format = xml output converter, for format definition see:
+ * http://docs.google.com/View?docid=dcc2jvzt_37hdzwkmf8
+ */
+class OutputXmlConverter extends OutputConverter {
+ private static $xmlVersion = '1.0';
+ private static $charSet = 'UTF-8';
+ private static $formatOutput = true;
+
+ // this maps the REST url to the xml tags
+ private static $entryTypes = array('people' => 'person', 'appdata' =>
'appdata',
+ 'activities' => 'activity', 'messages' => 'messages');
+ private $doc;
+
+ function outputResponse(ResponseItem $responseItem, RestRequestItem
$requestItem)
+ {
+ $doc = $this->createXmlDoc();
+ $requestType = $this->getRequestType($requestItem);
+ $data = $responseItem->getResponse();
+
+ // Check to see if this is a single entry, or a collection, and
construct either an xml
+ // feed (collection) or an entry (single)
+ if ($responseItem->getResponse() instanceof RestFulCollection) {
+ $totalResults =
$responseItem->getResponse()->getTotalResults();
+ $itemsPerPage = $requestItem->getCount();
+ $startIndex = $requestItem->getStartIndex();
+
+ // The root Feed element
+ $entry = $this->addNode($doc, 'response', '');
+
+ // Required Xml fields
+ $this->addNode($entry, 'startIndex', $startIndex);
+ $this->addNode($entry, 'itemsPerPage', $itemsPerPage);
+ $this->addNode($entry, 'totalResults', $totalResults);
+ $responses = $responseItem->getResponse()->getEntry();
+ foreach ($responses as $response) {
+ // recursively add responseItem data to the xml
structure
+ $this->addData($entry, $requestType, $response);
+ }
+ } else {
+ // Single entry = Xml:Entry
+ $entry = $this->addNode($doc, 'response', '');
+ // addData loops through the responseItem data
recursively creating a matching XML structure
+ $this->addData($entry, $requestType, $data);
+ }
+ $xml = $doc->saveXML();
+ echo $xml;
+ }
+
+ function outputBatch(Array $responses, SecurityToken $token)
+ {
+ $this->boundryHeaders();
+ foreach ($responses as $response) {
+ $request = $response['request'];
+ $response = $response['response'];
+ // output buffering supports multiple levels of it..
it's a nice feature to abuse :)
+ ob_start();
+ $this->outputResponse($response, $request);
+ $part = ob_get_contents();
+ ob_end_clean();
+ $this->outputPart($part, $response->getError());
+ }
+ }
+
+ /**
+ * Easy shortcut for creating & appending XML nodes
+ *
+ * @param DOMElement $node node to append the new child node too
+ * @param string $name name of the new element
+ * @param string $value value of the element, if empty no text node is
created
+ * @param array $attributes optional array of attributes, false by
default. If set attributes are added to the node using the key => val pairs
+ * @param string $nameSpace optional namespace to use when creating node
+ * @return DOMElement node
+ */
+ private function addNode($node, $name, $value = '', $attributes = false)
+ {
+ $childNode =
$node->appendChild($this->doc->createElement($name));
+ if (! empty($value) || $value == '0') {
+
$childNode->appendChild($this->doc->createTextNode($value));
+ }
+ if ($attributes && is_array($attributes)) {
+ foreach ($attributes as $attrName => $attrVal) {
+ $childNodeAttr =
$childNode->appendChild($this->doc->createAttribute($attrName));
+ if (! empty($attrVal)) {
+
$childNodeAttr->appendChild($this->doc->createTextNode($attrVal));
+ }
+ }
+ }
+ return $childNode;
+ }
+
+ /**
+ * Creates the root document using our xml version & charset
+ *
+ * @return DOMDocument
+ */
+ private function createXmlDoc()
+ {
+ $this->doc = new DOMDocument(self::$xmlVersion, self::$charSet);
+ $this->doc->formatOutput = self::$formatOutput;
+ return $this->doc;
+ }
+
+ /**
+ * Extracts the Xml entity name from the request url
+ *
+ * @param RequestItem $requestItem the request item
+ * @return string the request type
+ */
+ private function getRequestType($requestItem)
+ {
+ // map the Request URL to the content type to use
+ $params = $requestItem->getParameters();
+ if (! is_array($params) ||
empty(self::$entryTypes[$params[0]])) {
+ throw new Exception("Unsupported request type");
+ }
+ return self::$entryTypes[$params[0]];
+ }
+
+ /**
+ * Recursive function that maps an data array or object to it's xml
represantation
+ *
+ * @param DOMDocument $doc the root document
+ * @param DOMElement $element the element to append the new node(s) to
+ * @param string $name the name of the to be created node
+ * @param array or object $data the data to map to xml
+ * @param string $nameSpace if specified, the node is created using
this namespace
+ * @return DOMElement returns newly created element
+ */
+ private function addData(DOMElement $element, $name, $data)
+ {
+ $newElement =
$element->appendChild($this->doc->createElement($name));
+ if (is_array($data)) {
+ foreach ($data as $key => $val) {
+ if (is_array($val) || is_object($val)) {
+ // prevent invalid names.. try to guess
a good one :)
+ if (is_numeric($key)) {
+ $key = is_object($val) ?
get_class($val) : $key = $name;
+ }
+ $this->addData($newElement, $key, $val);
+ } else {
+ if (is_numeric($key)) {
+ $key = is_object($val) ?
get_class($val) : $key = $name;
+ }
+ $elm =
$newElement->appendChild($this->doc->createElement($key));
+
$elm->appendChild($this->doc->createTextNode($val));
+ }
+ }
+ } elseif (is_object($data)) {
+ if ($data instanceof Enum) {
+ // enums are output as : <NAME
key="$key">$displayValue</NAME>
+ $keyEntry =
$newElement->appendChild($this->doc->createAttribute('key'));
+
$keyEntry->appendChild($this->doc->createTextNode($data->key));
+
$newElement->appendChild($this->doc->createTextNode($data->getDisplayValue()));
+
+ } else {
+ $vars = get_object_vars($data);
+ foreach ($vars as $key => $val) {
+ if (is_array($val) || is_object($val)) {
+ // prevent invalid names.. try
to guess a good one :)
+ if (is_numeric($key)) {
+ $key = is_object($val)
? get_class($val) : $key = $name;
+ }
+ $this->addData($newElement,
$key, $val);
+ } else {
+ $elm =
$newElement->appendChild($this->doc->createElement($key));
+
$elm->appendChild($this->doc->createTextNode($val));
+ }
+ }
+ }
+ } else {
+
$newElement->appendChild($this->doc->createTextNode($data));
+ }
+ return $newElement;
+ }
+}
Modified: incubator/shindig/trunk/php/src/social-api/http/RestServlet.php
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/http/RestServlet.php?rev=689797&r1=689796&r2=689797&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/social-api/http/RestServlet.php (original)
+++ incubator/shindig/trunk/php/src/social-api/http/RestServlet.php Thu Aug 28
05:17:19 2008
@@ -50,9 +50,11 @@
require 'src/social-api/converters/OutputConverter.php';
require 'src/social-api/converters/OutputAtomConverter.php';
require 'src/social-api/converters/OutputJsonConverter.php';
+require 'src/social-api/converters/OutputXmlConverter.php';
require 'src/social-api/converters/InputConverter.php';
require 'src/social-api/converters/InputAtomConverter.php';
require 'src/social-api/converters/InputJsonConverter.php';
+require 'src/social-api/converters/InputXmlConverter.php';
require 'src/social-api/canonical/JsonDbOpensocialService.php';
class RestException extends Exception {}
@@ -110,6 +112,10 @@
$this->setContentType('application/atom+xml');
$outputConverter = new
OutputAtomConverter();
break;
+ case 'xml':
+
$this->setContentType('application/xml');
+ $outputConverter = new
OutputXmlConverter();
+ break;
default:
$this->outputError(new
ResponseItem(NOT_IMPLEMENTED, "Invalid output format"));
break;
@@ -315,6 +321,9 @@
case 'atom':
$inputConverter = new InputAtomConverter();
break;
+ case 'xml':
+ $inputConverter = new InputXmlConverter();
+ break;
default:
throw new Exception("Invalid or unsupported
input format");
break;