Added: 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/sample/CMISService.java
URL: 
http://svn.apache.org/viewvc/shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/sample/CMISService.java?rev=1028804&view=auto
==============================================================================
--- 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/sample/CMISService.java
 (added)
+++ 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/sample/CMISService.java
 Fri Oct 29 15:34:40 2010
@@ -0,0 +1,356 @@
+/*
+ * 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.extras.cmis.sample;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.chemistry.opencmis.client.api.Document;
+import org.apache.chemistry.opencmis.client.api.Folder;
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.common.util.ImmediateFuture;
+import org.apache.shindig.extras.as.sample.ActivityStreamsJsonDbService;
+import org.apache.shindig.extras.cmis.utils.CMISNotifier;
+import org.apache.shindig.extras.cmis.utils.CMISUtil;
+import org.apache.shindig.extras.cmis.utils.CMISWrapper;
+import org.apache.shindig.protocol.ProtocolException;
+import org.apache.shindig.protocol.RestfulCollection;
+import org.apache.shindig.protocol.conversion.BeanConverter;
+import org.apache.shindig.social.opensocial.model.Album;
+import org.apache.shindig.social.opensocial.model.MediaItem;
+import org.apache.shindig.social.opensocial.spi.AlbumService;
+import org.apache.shindig.social.opensocial.spi.CollectionOptions;
+import org.apache.shindig.social.opensocial.spi.GroupId;
+import org.apache.shindig.social.opensocial.spi.MediaItemService;
+import org.apache.shindig.social.opensocial.spi.UserId;
+import org.apache.shindig.social.sample.spi.JsonDbOpensocialService;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+/*
+ * Albums & MediaItems binding to a CMIS repository rather than the JSON DB.
+ * 
+ * TODO: Currently user agnostic; implement user comparisons.
+ * TODO: rather than returning null, throw errors
+ */
+public class CMISService implements AlbumService, MediaItemService {
+               
+       public static final String SERVICE_PROVIDER_ID = "ibmcmis.dnsdojo.com";
+
+       private BeanConverter converter;
+       
+       private CMISNotifier notifier;
+       
+       private static final String CMIS_REPO = 
"http://ibmcmis.dnsdojo.com:9090/p8cmis/resources/Service";;
+       
+       private final static String ROOT_PATH = "/OpenSocial";
+       
+       private static CMISWrapper cmis = new CMISWrapper(CMIS_REPO, ROOT_PATH);
+
+       @Inject
+       public CMISService(JsonDbOpensocialService jsonDb,
+                       @Named("shindig.bean.converter.json") BeanConverter 
converter) {
+               this.converter = converter;
+               this.notifier = new CMISNotifier(new 
ActivityStreamsJsonDbService(jsonDb, converter));
+       }
+
+       public Future<MediaItem> getMediaItem(UserId userId, String appId,
+                       String albumId, String mediaItemId, Set<String> fields,
+                       SecurityToken token) throws ProtocolException {
+               try {
+                       Folder folder = cmis.getFolder(cmis.getRootFolder(), 
albumId);
+                       if (folder == null) {
+                               throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                               "Album ID " + albumId + " does 
not exist");
+                       }
+                       Document document = cmis.getDocument(folder, 
mediaItemId);
+                       if (document == null) {
+                               throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                               "MediaItem ID " + mediaItemId + 
" does not exist");
+                       }
+                       MediaItem mediaItem = convertJson(
+                                       CMISUtil.documentToMediaItem(document), 
MediaItem.class);
+                       return ImmediateFuture.newInstance(mediaItem);
+               } catch (JSONException je) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                                       je.getMessage(), je);
+               }
+       }
+
+       public Future<RestfulCollection<MediaItem>> getMediaItems(UserId userId,
+                       String appId, String albumId, Set<String> mediaItemIds,
+                       Set<String> fields, CollectionOptions options, 
SecurityToken token)
+                       throws ProtocolException {
+               try {
+                       Folder folder = cmis.getFolder(cmis.getRootFolder(), 
albumId);
+                       List<Document> documents = cmis.getDocuments(folder);
+                       List<MediaItem> mediaItems = Lists.newArrayList();
+                       for (String mediaItemId : mediaItemIds) {
+                               boolean found = false;
+                               for (Document doc : documents) {
+                                       MediaItem mediaItem = convertJson(
+                                                       
CMISUtil.documentToMediaItem(doc), MediaItem.class);
+                                       if 
(mediaItem.getId().equals(mediaItemId)
+                                                       && 
mediaItem.getAlbumId().equals(albumId)) {
+                                               mediaItems.add(mediaItem);
+                                               found = true;
+                                               break;
+                                       }
+                               }
+
+                               // Error - mediaItemId not found
+                               if (!found) {
+                                       throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                                       "MediaItem ID " + 
mediaItemId + " does not exist");
+                               }
+                       }
+                       return ImmediateFuture.newInstance(new 
RestfulCollection<MediaItem>(
+                                       mediaItems));
+               } catch (JSONException je) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                                       je.getMessage(), je);
+               }
+       }
+
+       public Future<RestfulCollection<MediaItem>> getMediaItems(UserId userId,
+                       String appId, String albumId, Set<String> fields,
+                       CollectionOptions options, SecurityToken token) throws 
ProtocolException {
+               try {
+                       Folder folder = cmis.getFolder(cmis.getRootFolder(), 
albumId);
+                       if (folder == null) {
+                               throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                               "Album ID " + albumId + " does 
not exist");
+                       }
+                       List<Document> documents = cmis.getDocuments(folder);
+                       List<MediaItem> mediaItems = Lists.newArrayList();
+                       for (Document doc : documents) {
+                               
mediaItems.add(convertJson(CMISUtil.documentToMediaItem(doc),
+                                               MediaItem.class));
+                       }
+                       return ImmediateFuture.newInstance(new 
RestfulCollection<MediaItem>(
+                                       mediaItems));
+               } catch (JSONException je) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                                       je.getMessage(), je);
+               }
+       }
+
+       public Future<RestfulCollection<MediaItem>> getMediaItems(
+                       Set<UserId> userIds, GroupId groupId, String appId, 
Set<String> fields,
+                       CollectionOptions options, SecurityToken token) throws 
ProtocolException {
+               List<Folder> folders = cmis.getFolders(cmis.getRootFolder());
+               List<MediaItem> mediaItems = Lists.newArrayList();
+               try {
+                       for (Folder folder : folders) {
+                               List<Document> documents = 
cmis.getDocuments(folder);
+                               for (Document doc : documents) {
+                                       
mediaItems.add(convertJson(CMISUtil.documentToMediaItem(doc),
+                                                       MediaItem.class));
+                               }
+                       }
+                       return ImmediateFuture.newInstance(new 
RestfulCollection<MediaItem>(
+                                       mediaItems));
+               } catch (JSONException je) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                                       je.getMessage(), je);
+               }
+       }
+
+       public Future<Void> deleteMediaItem(UserId userId, String appId,
+                       String albumId, String mediaItemId, SecurityToken token)
+                       throws ProtocolException {
+               Folder folder = cmis.getFolder(cmis.getRootFolder(), albumId);
+               if (folder == null) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                       "Album ID " + albumId + " does not 
exist");
+               }
+               Document document = cmis.getDocument(folder, mediaItemId);
+               if (document == null) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                       "MediaItem ID " + mediaItemId + " does 
not exist");
+               }
+               
+               // ActivityStream notification
+               try {
+                       MediaItem mediaItem = 
convertJson(CMISUtil.documentToMediaItem(document), MediaItem.class);
+                       notifier.spitActivityEntry(userId, token, mediaItem, 
"delete", "MediaItem deleted", SERVICE_PROVIDER_ID, CMIS_REPO);
+               } catch (JSONException e) {
+                       e.printStackTrace();
+               }
+               
+               cmis.deleteDocument(document);
+               return ImmediateFuture.newInstance(null);
+       }
+
+       public Future<Void> createMediaItem(UserId userId, String appId,
+                       String albumId, MediaItem mediaItem, SecurityToken 
token)
+                       throws ProtocolException {
+               Folder folder = cmis.getFolder(cmis.getRootFolder(), albumId);
+               if (folder == null) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                       "Album ID " + albumId + " does not 
exist");
+               }
+               Map<String, String> docProps = 
CMISUtil.mediaItemToDocumentProperties(mediaItem);
+               cmis.createDocument(folder, docProps);
+               
+               // ActivityStream notification
+               notifier.spitActivityEntry(userId, token, mediaItem, "post", 
"MediaItem created", SERVICE_PROVIDER_ID, CMIS_REPO);
+               return ImmediateFuture.newInstance(null);
+       }
+
+       public Future<Void> updateMediaItem(UserId userId, String appId,
+                       String albumId, String mediaItemId, MediaItem mediaItem,
+                       SecurityToken token) throws ProtocolException {
+               Folder folder = cmis.getFolder(cmis.getRootFolder(), albumId);
+               if (folder == null) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                       "Album ID " + albumId + " does not 
exist");
+               }
+               Document document = cmis.getDocument(folder, mediaItemId);
+               if (document == null) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                       "MediaItem ID " + mediaItemId + " does 
not exist");
+               }
+               Map<String, String> docProps = 
CMISUtil.mediaItemToDocumentProperties(mediaItem);
+               cmis.updateProperties(document, docProps);
+               
+               // ActivityStream notification
+               notifier.spitActivityEntry(userId, token, mediaItem, "update", 
"MediaItem updated", SERVICE_PROVIDER_ID, CMIS_REPO);
+               return ImmediateFuture.newInstance(null);
+       }
+
+       public Future<Album> getAlbum(UserId userId, String appId,
+                       Set<String> fields, String albumId, SecurityToken token)
+                       throws ProtocolException {
+               try {
+                       Folder folder = cmis.getFolder(cmis.getRootFolder(), 
albumId);
+                       if (folder == null) {
+                               throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                               "Album ID " + albumId + " does 
not exist");
+                       }
+                       return 
ImmediateFuture.newInstance(convertJson(CMISUtil.folderToAlbum(folder), 
Album.class));
+               } catch (JSONException je) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                                       je.getMessage(), je);
+               }
+       }
+
+       public Future<RestfulCollection<Album>> getAlbums(UserId userId,
+                       String appId, Set<String> fields, CollectionOptions 
options,
+                       Set<String> albumIds, SecurityToken token) throws 
ProtocolException {
+               List<Folder> folders = cmis.getFolders(cmis.getRootFolder());
+               List<Album> albums = Lists.newArrayList();
+               try {
+                       for (String albumId : albumIds) {
+                               boolean found = false;
+                               for (Folder folder : folders) {
+                                       if (albumId.equals(folder.getId())) {
+                                               
albums.add(convertJson(CMISUtil.folderToAlbum(folder),
+                                                               Album.class));
+                                               found = true;
+                                               break;
+                                       }
+                               }
+
+                               // Error - albumId not found
+                               if (!found) {
+                                       throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                                       "Album ID " + albumId + 
" does not exist");
+                               }
+                       }
+                       return ImmediateFuture.newInstance(new 
RestfulCollection<Album>(albums));
+               } catch (JSONException je) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                                       je.getMessage(), je);
+               }
+       }
+
+       public Future<RestfulCollection<Album>> getAlbums(Set<UserId> userIds,
+                       GroupId groupId, String appId, Set<String> fields,
+                       CollectionOptions options, SecurityToken token) throws 
ProtocolException {
+               List<Folder> folders = cmis.getFolders(cmis.getRootFolder());
+               List<Album> albums = Lists.newArrayList();
+               try {
+                       for (Folder folder : folders) {
+                               
albums.add(convertJson(CMISUtil.folderToAlbum(folder), Album.class));
+                       }
+                       return ImmediateFuture.newInstance(new 
RestfulCollection<Album>(albums));
+               } catch (JSONException je) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                                       je.getMessage(), je);
+               }
+       }
+
+       public Future<Void> deleteAlbum(UserId userId, String appId, String 
albumId,
+                       SecurityToken token) throws ProtocolException {
+               Folder folder = cmis.getFolder(cmis.getRootFolder(), albumId);
+               if (folder == null) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                       "Album ID " + albumId + " does not 
exist");
+               }
+
+               try {
+                       Album album = 
convertJson(CMISUtil.folderToAlbum(folder), Album.class);
+                       List<String> verbs = new ArrayList<String>();
+                       verbs.add("delete");
+                       notifier.spitActivityEntry(userId, token, album, 
"delete", "Album deleted", SERVICE_PROVIDER_ID, CMIS_REPO);
+               } catch (JSONException e) {
+                       e.printStackTrace();
+               }
+               cmis.deleteFolder(folder);
+               return ImmediateFuture.newInstance(null);
+       }
+
+       public Future<Void> createAlbum(UserId userId, String appId, Album 
album,
+                       SecurityToken token) throws ProtocolException {
+               Map<String, String> folderProps = 
CMISUtil.albumToFolderProperties(album);
+               Folder folder = cmis.createFolder(cmis.getRootFolder(), 
folderProps);
+               notifier.spitActivityEntry(userId, token, album, "post", "Album 
created", SERVICE_PROVIDER_ID, CMIS_REPO);
+               if (folder != null) { // TODO: why is folder null?  move AS 
code here once fixed
+                       return ImmediateFuture.newInstance(null);
+               }
+               return null; // Error
+       }
+
+       public Future<Void> updateAlbum(UserId userId, String appId, Album 
album,
+                       String albumId, SecurityToken token) throws 
ProtocolException {
+               Folder folder = cmis.getFolder(cmis.getRootFolder(), albumId);
+               if (folder == null) {
+                       throw new 
ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
+                                       "Album ID " + albumId + " does not 
exist");
+               }
+               cmis.updateProperties(folder, 
CMISUtil.albumToFolderProperties(album));
+               notifier.spitActivityEntry(userId, token, album, "update", 
"Album updated", SERVICE_PROVIDER_ID, CMIS_REPO);
+               return ImmediateFuture.newInstance(null);
+       }
+
+       private <T> T convertJson(JSONObject object, Class<T> clz)
+                       throws JSONException {
+               return converter.convertToObject(object.toString(), clz);
+       }
+}

Added: 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISNotifier.java
URL: 
http://svn.apache.org/viewvc/shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISNotifier.java?rev=1028804&view=auto
==============================================================================
--- 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISNotifier.java
 (added)
+++ 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISNotifier.java
 Fri Oct 29 15:34:40 2010
@@ -0,0 +1,116 @@
+/*
+ * 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.extras.cmis.utils;
+
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.extras.as.core.model.ActivityEntryImpl;
+import org.apache.shindig.extras.as.core.model.ActivityObjectImpl;
+import org.apache.shindig.extras.as.core.model.MediaLinkImpl;
+import org.apache.shindig.extras.as.opensocial.model.ActivityEntry;
+import org.apache.shindig.extras.as.opensocial.model.ActivityObject;
+import org.apache.shindig.extras.as.opensocial.model.MediaLink;
+import org.apache.shindig.extras.as.opensocial.spi.ActivityStreamService;
+import org.apache.shindig.social.opensocial.model.Album;
+import org.apache.shindig.social.opensocial.model.MediaItem;
+import org.apache.shindig.social.opensocial.spi.UserId;
+
+/*
+ * Manages the creation and notification of ActivityStreams for the CMIS
+ * service.
+ */
+public class CMISNotifier {
+       
+       private boolean enabled;
+       
+       private ActivityStreamService service;
+       
+       public CMISNotifier(ActivityStreamService service) {
+               this.service = service;
+               this.enabled = true;
+       }
+       
+       public CMISNotifier(ActivityStreamService service, boolean enabled) {
+               this.service = service;
+               this.enabled = enabled;
+       }
+       
+       public void spitActivityEntry(UserId userId, SecurityToken token,
+                       MediaItem mediaItem, String verb, String title,
+                       String serviceProviderId, String serviceProviderUrl) {
+               if (!enabled) return;
+               ActivityEntry entry = new ActivityEntryImpl();
+               entry.setTitle(title);
+               entry.setPostedTime("" + System.currentTimeMillis());   // 
TODO: extract, correct time format?
+               entry.setVerb(verb);
+               ActivityObject actor = new ActivityObjectImpl();
+               actor.setId(userId.getUserId());
+               entry.setActor(actor);                                          
                                                        // TODO: extract
+               ActivityObject object = new ActivityObjectImpl();
+               object.setDisplayName(mediaItem.getTitle());
+               object.setId(mediaItem.getId());
+               object.setSummary(mediaItem.getDescription());
+               object.setLink(mediaItem.getUrl());
+               MediaLink mediaLink = new MediaLinkImpl();
+               mediaLink.setDuration(mediaItem.getDuration());
+               object.setMedia(mediaLink);
+               object.setObjectType("file");
+               ActivityObject serviceProvider = new ActivityObjectImpl();
+               serviceProvider.setId(serviceProviderId);
+               serviceProvider.setDisplayName(serviceProviderId);
+               serviceProvider.setLink(serviceProviderUrl);
+               entry.setProvider(serviceProvider);
+               entry.setObject(object);
+               service.createActivityEntry(userId, null, null, null, entry, 
token);
+       }
+       
+       public void spitActivityEntry(UserId userId, SecurityToken token,
+                       Album album, String verb, String title,
+                       String serviceProviderId, String serviceProviderUrl) {
+               if (!enabled) return;
+               ActivityObject object = new ActivityObjectImpl();
+               object.setSummary(album.getDescription());
+               object.setId(album.getId());
+               object.setObjectType("folder");
+               ActivityObject actor = new ActivityObjectImpl();
+               actor.setId(userId.getUserId());
+               object.setDisplayName(album.getTitle());
+               ActivityObject serviceProvider = new ActivityObjectImpl();
+               serviceProvider.setId(serviceProviderId);
+               serviceProvider.setDisplayName(serviceProviderId);
+               serviceProvider.setLink(serviceProviderUrl);
+               ActivityEntry entry = new ActivityEntryImpl();
+               entry.setTitle(title);
+               entry.setProvider(serviceProvider);
+               entry.setObject(object);
+               entry.setActor(actor);
+               entry.setVerb(verb);
+               service.createActivityEntry(userId, null, null, null, entry, 
token);
+       }       
+       public ActivityStreamService getService() {
+               return this.service;
+       }
+       
+       public void enable() {
+               this.enabled = true;
+       }
+       
+       public void disable() {
+               this.enabled = false;
+       }
+}

Added: 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISUtil.java
URL: 
http://svn.apache.org/viewvc/shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISUtil.java?rev=1028804&view=auto
==============================================================================
--- 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISUtil.java
 (added)
+++ 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISUtil.java
 Fri Oct 29 15:34:40 2010
@@ -0,0 +1,166 @@
+/*
+ * 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.extras.cmis.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.chemistry.opencmis.client.api.Document;
+import org.apache.chemistry.opencmis.client.api.Folder;
+import org.apache.chemistry.opencmis.commons.PropertyIds;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+import org.apache.shindig.social.opensocial.model.Album;
+import org.apache.shindig.social.opensocial.model.MediaItem;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/*
+ * Mapping utilities for conversion between OpenSocial Albums & MediaItems and
+ * CMIS Folders & Documents, respectively.
+ */
+public class CMISUtil {
+
+       /*
+        * Maps a CMIS Document to an OpenSocial MediaItem.
+        * 
+        * TODO: representing time correctly?
+        * TODO: thumbnailUrl & URL: query, feed, or parse OR 
<base>/objectId/majorVersion/filename?
+        * 
+        * @param doc is the Document to map to a MediaItem
+        * 
+        * @return JSONObject represents the MediaItem as a JSONObject
+        */
+       public static JSONObject documentToMediaItem(Document doc) {
+               try {
+                       JSONObject mediaItem = new JSONObject();
+                       mediaItem.put("albumId", 
doc.getParents().get(0).getId());      // albumId <--> parent's ID
+                       mediaItem.put("created", 
doc.getCreationDate().getTime().toString());   // created <--> creationDate
+                       mediaItem.put("description", doc.getCheckinComment());  
        // description <--> checkinComment
+                       mediaItem.put("fileSize", 
doc.getContentStreamLength());        // fileSize <--> contentStreamLength
+                       mediaItem.put("id", doc.getId());                       
                                // id <--> objectId
+                       mediaItem.put("lastUpdated", 
doc.getLastModificationDate().getTime().toString());       // lastUpdated <--> 
lastModificationDate
+                       mediaItem.put("mimeType", 
doc.getContentStreamMimeType());      // mimeType <--> contentStreamMimeType
+                       mediaItem.put("title", doc.getName());                  
                        // title <--> name
+                       String type = 
getMediaType(doc.getContentStreamMimeType());
+                       if (type != null) mediaItem.put("type", type);          
                // type <--> contentStreamMimeType (parsed)
+                       // TODO: MediaItem does not have ownerId, otherwise 
ownerId <--> createdBy
+                       return mediaItem;
+               } catch (JSONException e) {
+                       e.printStackTrace();
+               }
+               return null;    // Error
+       }
+       
+       /*
+        * Maps a CMIS Folder to an OpenSocial Album.
+        * 
+        * @param folder is the Folder to map to an Album
+        * 
+        * @return JSONObject represents the Album as a JSONObject
+        */
+       public static JSONObject folderToAlbum(Folder folder) {
+               try {
+                       JSONObject album = new JSONObject();
+                       album.put(Album.Field.TITLE.toString(), 
folder.getName());
+                       album.put(Album.Field.ID.toString(), folder.getId());
+                       return album;
+               } catch (JSONException e) {
+                       e.printStackTrace();
+               }
+               return null;
+       }
+       
+       /*
+        * Maps a MediaItem to properties that define a CMIS Document.
+        * 
+        * @param mediaItem is the MediaItem to map to CMIS Document properties
+        * 
+        * @return Map<String, String> are the properties defining a CMIS 
Document 
+        */
+       public static Map<String, String> 
mediaItemToDocumentProperties(MediaItem mediaItem) {
+               // Compose properties associated with document
+               Map<String, String> properties = new HashMap<String, String>();
+               properties.put(PropertyIds.OBJECT_TYPE_ID, 
BaseTypeId.CMIS_DOCUMENT.value());
+               put(properties, PropertyIds.CREATION_DATE, 
mediaItem.getCreated());
+               put(properties, PropertyIds.CHECKIN_COMMENT, 
mediaItem.getDescription());
+               put(properties, PropertyIds.CONTENT_STREAM_LENGTH, 
mediaItem.getFileSize());
+               put(properties, PropertyIds.OBJECT_ID, mediaItem.getId());
+               put(properties, PropertyIds.LAST_MODIFICATION_DATE, 
mediaItem.getLastUpdated());
+               put(properties, PropertyIds.CONTENT_STREAM_MIME_TYPE, 
mediaItem.getMimeType());
+               put(properties, PropertyIds.NAME, mediaItem.getTitle());
+               put(properties, PropertyIds.CONTENT_STREAM_ID, 
mediaItem.getUrl());
+               return properties;
+       }
+       
+       /*
+        * Maps an Album to properties that define a CMIS Folder.
+        * 
+        * @param album is the Album to map to CMIS Folder properties
+        * 
+        * @return Map<String, String> are the properties that define a CMIS 
Folder
+        */
+       public static Map<String, String> albumToFolderProperties(Album album) {
+               Map<String, String> properties = new HashMap<String, String>();
+               properties.put(PropertyIds.OBJECT_TYPE_ID, 
BaseTypeId.CMIS_FOLDER.value());
+               put(properties, PropertyIds.NAME, album.getTitle());
+               put(properties, PropertyIds.OBJECT_ID, album.getId());
+               return properties;
+       }
+       
+       //---------------------------- PRIVATE HELPERS 
----------------------------
+       
+       /*
+        * Puts the given value to the given property if value is non-null and
+        * non-empty.
+        * 
+        * @param map is the map to put the value into
+        * @param prop is the property to put the value to
+        * @param value is the value to put for the property
+        * 
+        * @return true if the value was put, false otherwise
+        */
+       private static boolean put(Map<String, String> map, String prop, String 
value) {
+               if (value != null && !value.isEmpty()) {
+                       map.put(prop, value);
+                       return true;
+               }
+               return false;
+       }
+               
+       /*
+        * Translates a MIME type into a MediaItem type.
+        * 
+        * @param mimeType is the MIME type to translate
+        * 
+        * @return a String representing a MediaItem type
+        */
+       private static String getMediaType(String mimeType) {
+               if (mimeType == null) {
+                       return null;
+               } else if (mimeType.contains("audio")) {
+                       return MediaItem.Type.AUDIO.toString();
+               } else if (mimeType.contains("video")) {
+                       return MediaItem.Type.VIDEO.toString();
+               } else if (mimeType.contains("image")) {
+                       return MediaItem.Type.IMAGE.toString();
+               } else {
+                       return null;
+               }
+       }
+}

Added: 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISWrapper.java
URL: 
http://svn.apache.org/viewvc/shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISWrapper.java?rev=1028804&view=auto
==============================================================================
--- 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISWrapper.java
 (added)
+++ 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/CMISWrapper.java
 Fri Oct 29 15:34:40 2010
@@ -0,0 +1,186 @@
+/*
+ * 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.extras.cmis.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.chemistry.opencmis.client.api.CmisObject;
+import org.apache.chemistry.opencmis.client.api.Document;
+import org.apache.chemistry.opencmis.client.api.Folder;
+import org.apache.chemistry.opencmis.client.api.Repository;
+import org.apache.chemistry.opencmis.client.api.Session;
+import org.apache.chemistry.opencmis.client.api.SessionFactory;
+import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
+import org.apache.chemistry.opencmis.commons.SessionParameter;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+import org.apache.chemistry.opencmis.commons.enums.BindingType;
+import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
+import 
org.apache.chemistry.opencmis.commons.exceptions.CmisConnectionException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
+
+/*
+ * Wraps high-level interaction with a CMIS repository.
+ */
+public class CMISWrapper {
+       
+       private Session session;
+       private Folder root;
+       private String repo;
+       private String rootPath;
+       
+       /*
+        * Constructs the CMIS wrapper with the given repository.
+        * 
+        * @param cmisRepo references the repository to connect
+        */
+       public CMISWrapper(String cmisRepo, String rootPath) {
+               this.repo = cmisRepo;
+               this.rootPath = rootPath;
+               this.session = newSession();
+               this.root = getRoot();
+       }
+       
+       public Folder getRootFolder() {
+               return root;
+       }
+       
+       public List<Folder> getFolders(Folder parent) {
+               List<Folder> folders = new ArrayList<Folder>();
+               Iterator<CmisObject> children = parent.getChildren().iterator();
+               while (children.hasNext()) {
+                       CmisObject child = children.next();
+                       if (child.getBaseTypeId() == BaseTypeId.CMIS_FOLDER) {
+                               folders.add((Folder)child);
+                       }
+               }
+               return folders;
+       }
+       
+       public Folder getFolder(Folder parent, String id) {
+               List<Folder> folders = getFolders(parent);
+               for (Folder folder : folders) {
+                       if (folder.getId().equals(id)) {
+                               return folder;
+                       }
+               }
+               return null;
+       }
+       
+       public List<Document> getDocuments(Folder parent) {
+               List<Document> docs = new ArrayList<Document>();
+               Iterator<CmisObject> children = parent.getChildren().iterator();
+               while (children.hasNext()) {
+                       CmisObject child = children.next();
+                       if (child.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT) {
+                               docs.add((Document)child);
+                       }
+               }
+               return docs;
+       }
+       
+       public Document getDocument(Folder parent, String id) {
+               List<Document> documents = getDocuments(parent);
+               for (Document document : documents) {
+                       if (document.getId().equals(id)) {
+                               return document;
+                       }
+               }
+               return null;
+       }
+       
+       public Document createDocument(Folder parent, Map<String, String> 
properties) {
+               return parent.createDocument(properties, null, null, null, 
null, null, null);
+       }
+       
+       public Folder createFolder(Folder parent, Map<String, String> 
properties) {
+               return parent.createFolder(properties, null, null, null, null);
+       }
+       
+       public void deleteDocument(Document doc) {
+               doc.delete(true);
+       }
+       
+       public void deleteFolder(Folder folder) {
+               folder.deleteTree(true, UnfileObject.DELETE, true);
+       }
+       
+       public void updateProperties(CmisObject object, Map<String, String> 
props) {
+               object.updateProperties(props);
+       }
+       
+       public Folder getParent(Document child) {
+               return child.getParents().get(0);
+       }
+       
+       public Folder getParent(Folder child) {
+               return child.getParents().get(0);
+       }
+       
+       // ---------------------------- PRIVATE HELPERS 
---------------------------
+       private Session newSession() {
+               // Default factory implementation of client runtime.
+               SessionFactory sessionFactory = 
SessionFactoryImpl.newInstance();
+               Map<String, String> parameter = new HashMap<String, String>();
+
+               // User credentials.
+               //parameter.put(SessionParameter.USER, "dummyuser");
+               //parameter.put(SessionParameter.PASSWORD, "dummysecret");
+
+               // Connection settings.
+               parameter.put(SessionParameter.ATOMPUB_URL, this.repo); // URL 
to CMIS server.
+               // parameter.put(SessionParameter.REPOSITORY_ID, 
"myRepository"); // Only necessary if there are more than one repository.
+               parameter.put(SessionParameter.BINDING_TYPE, 
BindingType.ATOMPUB.value());
+
+               // Session locale.
+               parameter.put(SessionParameter.LOCALE_ISO3166_COUNTRY, "");
+               parameter.put(SessionParameter.LOCALE_ISO639_LANGUAGE, "en");
+               parameter.put(SessionParameter.LOCALE_VARIANT, "US");
+
+               // Create session.
+               Session session = null;
+               try {
+                       // This supposes only one repository is available at 
the URL.
+                       Repository soleRepository = 
sessionFactory.getRepositories(parameter).get(0);
+                       session = soleRepository.createSession();
+               }
+               catch(CmisConnectionException e) { 
+                       // The server is unreachable
+                       e.printStackTrace();
+               }
+               catch(CmisRuntimeException e) {
+                       // The user/password have probably been rejected by the 
server.
+                       e.printStackTrace();
+               }
+               
+               return session;
+       }
+       
+       /*
+        * Returns the root directory for working with the CMIS repository.  
Note
+        * that this is not necessarily the real repository's root, but it's the
+        * root for testing functionality.
+        */
+       private Folder getRoot() {
+               return (Folder)session.getObjectByPath(rootPath);
+       }
+}

Added: 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/RepoUtil.java
URL: 
http://svn.apache.org/viewvc/shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/RepoUtil.java?rev=1028804&view=auto
==============================================================================
--- 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/RepoUtil.java
 (added)
+++ 
shindig/sandbox/trunk/cmis-binding/src/main/java/org/apache/shindig/extras/cmis/utils/RepoUtil.java
 Fri Oct 29 15:34:40 2010
@@ -0,0 +1,67 @@
+/*
+ * 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.extras.cmis.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.chemistry.opencmis.commons.PropertyIds;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+
+/*
+ * Utility for manipulating a live CMIS repository (e.g. recreating a folder).
+ */
+public class RepoUtil {
+       
+       private final static String REPO = 
"http://ibmcmis.dnsdojo.com:9090/p8cmis/resources/Service";;
+       private static CMISWrapper cmisWrapper;
+
+       public static void main(String[] args) {
+               cmisWrapper = new CMISWrapper(REPO, "/");
+               Map<String, String> folderProps = 
createFolderProps("OpenSocial", null);
+               cmisWrapper.createFolder(cmisWrapper.getRootFolder(), 
folderProps);
+       }
+       
+       private static Map<String, String> createFolderProps(String name, 
String id) {
+               Map<String, String> properties = new HashMap<String, String>();
+               properties.put(PropertyIds.OBJECT_TYPE_ID, 
BaseTypeId.CMIS_FOLDER.value());
+               put(properties, PropertyIds.NAME, name);
+               put(properties, PropertyIds.OBJECT_ID, null);
+               return properties;
+       }
+       
+       // ---------------------------- PRIVATE HELPERS 
---------------------------     
+       /*
+        * Puts the given value to the given property if value is non-null and
+        * non-empty.
+        * 
+        * @param map is the map to put the value into
+        * @param prop is the property to put the value to
+        * @param value is the value to put for the property
+        * 
+        * @return true if the value was put, false otherwise
+        */
+       private static boolean put(Map<String, String> map, String prop, String 
value) {
+               if (value != null && !value.isEmpty()) {
+                       map.put(prop, value);
+                       return true;
+               }
+               return false;
+       }
+}

Added: shindig/sandbox/trunk/cmis-binding/src/main/webapp/WEB-INF/web.xml
URL: 
http://svn.apache.org/viewvc/shindig/sandbox/trunk/cmis-binding/src/main/webapp/WEB-INF/web.xml?rev=1028804&view=auto
==============================================================================
--- shindig/sandbox/trunk/cmis-binding/src/main/webapp/WEB-INF/web.xml (added)
+++ shindig/sandbox/trunk/cmis-binding/src/main/webapp/WEB-INF/web.xml Fri Oct 
29 15:34:40 2010
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns="http://java.sun.com/xml/ns/javaee"; 
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"; 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"; id="Shindig" version="2.5">
+  <display-name>Shindig</display-name>
+  <context-param>
+    <param-name>guice-modules</param-name>
+    <param-value>
+      org.apache.shindig.common.PropertiesModule:
+      org.apache.shindig.gadgets.DefaultGuiceModule:
+      org.apache.shindig.social.core.config.SocialApiGuiceModule:
+      org.apache.shindig.extras.cmis.CMISGuiceModule:
+      org.apache.shindig.gadgets.oauth.OAuthModule:
+      org.apache.shindig.common.cache.ehcache.EhCacheModule:
+      org.apache.shindig.extras.ShindigExtrasGuiceModule:
+      org.apache.shindig.extras.as.ActivityStreamsGuiceModule:
+    </param-value>
+  </context-param>
+  <context-param>
+    <param-name>system.properties</param-name>
+    <param-value>
+       shindig.host=localhost
+       shindig.port=8080       
+     </param-value>
+  </context-param>
+  <filter>
+    <filter-name>authFilter</filter-name>
+    
<filter-class>org.apache.shindig.auth.AuthenticationServletFilter</filter-class>
+  </filter>
+  <filter-mapping>
+    <filter-name>authFilter</filter-name>
+    <url-pattern>/social/*</url-pattern>
+    <url-pattern>/gadgets/ifr</url-pattern>
+    <url-pattern>/gadgets/makeRequest</url-pattern>
+    <url-pattern>/gadgets/api/rpc/*</url-pattern>
+    <url-pattern>/gadgets/api/rest/*</url-pattern>
+    <url-pattern>/rpc/*</url-pattern>
+    <url-pattern>/rest/*</url-pattern>
+  </filter-mapping>
+  <listener>
+    
<listener-class>org.apache.shindig.common.servlet.GuiceServletContextListener</listener-class>
+  </listener>
+  <servlet>
+    <servlet-name>xml-to-html</servlet-name>
+    <servlet-class>
+      org.apache.shindig.gadgets.servlet.GadgetRenderingServlet
+    </servlet-class>
+  </servlet>
+  <servlet>
+    <servlet-name>accel</servlet-name>
+    <servlet-class>
+      org.apache.shindig.gadgets.servlet.HtmlAccelServlet
+    </servlet-class>
+  </servlet>
+  <servlet>
+    <servlet-name>proxy</servlet-name>
+    <servlet-class>
+      org.apache.shindig.gadgets.servlet.ProxyServlet
+    </servlet-class>
+  </servlet>
+  <servlet>
+    <servlet-name>makeRequest</servlet-name>
+    <servlet-class>
+      org.apache.shindig.gadgets.servlet.MakeRequestServlet
+    </servlet-class>
+  </servlet>
+  <servlet>
+    <servlet-name>concat</servlet-name>
+    <servlet-class>
+      org.apache.shindig.gadgets.servlet.ConcatProxyServlet
+    </servlet-class>
+  </servlet>
+  <servlet>
+    <servlet-name>oauthCallback</servlet-name>
+    <servlet-class>
+      org.apache.shindig.gadgets.servlet.OAuthCallbackServlet
+    </servlet-class>
+  </servlet>
+  <servlet>
+    <servlet-name>metadata</servlet-name>
+    <servlet-class>
+      org.apache.shindig.gadgets.servlet.RpcServlet
+    </servlet-class>
+  </servlet>
+  <servlet>
+    <servlet-name>js</servlet-name>
+    <servlet-class>org.apache.shindig.gadgets.servlet.JsServlet</servlet-class>
+  </servlet>
+  <servlet>
+    <servlet-name>restapiServlet</servlet-name>
+    <servlet-class>
+      org.apache.shindig.protocol.DataServiceServlet
+    </servlet-class>
+    <init-param>
+      <param-name>handlers</param-name>
+      <param-value>org.apache.shindig.handlers</param-value>
+    </init-param>
+  </servlet>
+  <servlet>
+    <servlet-name>jsonRpcServlet</servlet-name>
+    <servlet-class>
+      org.apache.shindig.protocol.JsonRpcServlet
+    </servlet-class>
+    <init-param>
+      <param-name>handlers</param-name>
+      <param-value>org.apache.shindig.handlers</param-value>
+    </init-param>
+  </servlet>
+  <servlet>
+    <servlet-name>sampleOAuth</servlet-name>
+    <servlet-class>
+      org.apache.shindig.social.sample.oauth.SampleOAuthServlet
+    </servlet-class>
+  </servlet>
+  <servlet-mapping>
+    <servlet-name>js</servlet-name>
+    <url-pattern>/gadgets/js/*</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>proxy</servlet-name>
+    <url-pattern>/gadgets/proxy/*</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>makeRequest</servlet-name>
+    <url-pattern>/gadgets/makeRequest</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>jsonRpcServlet</servlet-name>
+    <url-pattern>/rpc/*</url-pattern>
+    <url-pattern>/gadgets/api/rpc/*</url-pattern>
+    <url-pattern>/social/rpc/*</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>restapiServlet</servlet-name>
+    <url-pattern>/rest/*</url-pattern>
+    <url-pattern>/gadgets/api/rest/*</url-pattern>
+    <url-pattern>/social/rest/*</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>concat</servlet-name>
+    <url-pattern>/gadgets/concat</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>oauthCallback</servlet-name>
+    <url-pattern>/gadgets/oauthcallback</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>xml-to-html</servlet-name>
+    <url-pattern>/gadgets/ifr</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>accel</servlet-name>
+    <url-pattern>/gadgets/accel</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>metadata</servlet-name>
+    <url-pattern>/gadgets/metadata</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>sampleOAuth</servlet-name>
+    <url-pattern>/oauth/*</url-pattern>
+  </servlet-mapping>
+</web-app>
\ No newline at end of file


Reply via email to