/* * Copyright (c) 2011 Enrico Franchi, Michele Tomaiuolo and University of Parma. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package net.blogracy.controller; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.security.InvalidParameterException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.TimeZone; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.DeliveryMode; import javax.jms.Destination; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import net.blogracy.config.Configurations; import net.blogracy.util.FileUtils; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.commons.codec.binary.Base32; import org.apache.shindig.protocol.conversion.BeanConverter; import org.apache.shindig.protocol.conversion.BeanJsonConverter; import org.apache.shindig.social.core.model.ActivityEntryImpl; import org.apache.shindig.social.core.model.ActivityObjectImpl; import org.apache.shindig.social.core.model.AlbumImpl; import org.apache.shindig.social.core.model.MediaItemImpl; import org.apache.shindig.social.opensocial.model.ActivityEntry; import org.apache.shindig.social.opensocial.model.ActivityObject; import org.apache.shindig.social.opensocial.model.Album; import org.apache.shindig.social.opensocial.model.MediaItem; import org.apache.shindig.social.opensocial.model.MediaItem.Type; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; import com.google.inject.Binder; import com.google.inject.Guice; import com.google.inject.Module; import com.google.inject.name.Names; /** * Generic functions to manipulate feeds are defined in this class. */ public class MediaController { static final DateFormat ISO_DATE_FORMAT = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss'Z'"); static final String CACHE_FOLDER = Configurations.getPathConfig() .getCachedFilesDirectoryPath(); private static final MediaController theInstance = new MediaController(); private static final FileSharing sharing = new FileSharing(); private static final ActivitiesController activities = new ActivitiesController(); private static final DistributedHashTable dht = new DistributedHashTable(); private static BeanJsonConverter CONVERTER = new BeanJsonConverter( Guice.createInjector(new Module() { @Override public void configure(Binder b) { b.bind(BeanConverter.class) .annotatedWith( Names.named("shindig.bean.converter.json")) .to(BeanJsonConverter.class); } })); public static MediaController getSingleton() { return theInstance; } public MediaController() { ISO_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); } /** * Create a new Album for the user. Adds the album to the user's recordDB * entry Adds the action to the ActivityStream (verb: create) * * @param userId * @param photoAlbumName */ public synchronized String createPhotoAlbum(String userId, String photoAlbumTitle) { if (userId == null) throw new InvalidParameterException("userId cannot be null"); if (photoAlbumTitle == null || photoAlbumTitle.isEmpty()) return null; String albumHash = null; try { albumHash = sharing.hash(userId + photoAlbumTitle); Album album = new AlbumImpl(); album.setTitle(photoAlbumTitle); album.setId(albumHash); album.setOwnerId(userId); List<Type> types = new ArrayList<Type>(); types.add(Type.IMAGE); album.setMediaType(types); // Album is empty where created album.setMediaItemCount(0); final List<ActivityEntry> feed = activities.getFeed(userId); final ActivityEntry entry = new ActivityEntryImpl(); entry.setVerb("create"); ActivityObject mediaAlbumObject = new ActivityObjectImpl(); mediaAlbumObject.setObjectType("collection"); mediaAlbumObject.setContent(photoAlbumTitle); entry.setObject(mediaAlbumObject); entry.setPublished(ISO_DATE_FORMAT.format(new Date())); entry.setContent(photoAlbumTitle); feed.add(0, entry); String feedUri = activities.seedActivityStream(userId, feed); // Append another album into the user's recordDB JSONObject recordDb = DistributedHashTable.getSingleton() .getRecord(userId); if (recordDb == null) recordDb = new JSONObject(); JSONArray albums = recordDb.optJSONArray("albums"); if (albums != null) { // Simply append new album albums.put(new JSONObject(CONVERTER.convertToString(album))); } else { albums = new JSONArray(); albums.put(new JSONObject(CONVERTER.convertToString(album))); } DistributedHashTable.getSingleton().store(userId, feedUri, entry.getPublished(), albums, recordDb.optJSONArray("mediaItems")); } catch (Exception e) { e.printStackTrace(); } return albumHash; } /** * Get the photo albums from recordDb given the userId. * * @param userId */ public List<Album> getAlbums(String userId) { if (userId == null) throw new InvalidParameterException("userId cannot be null"); List<Album> albums = new ArrayList<Album>(); try { JSONObject recordDb = DistributedHashTable.getSingleton() .getRecord(userId); if (recordDb == null) return albums; JSONArray albumArray = recordDb.optJSONArray("albums"); if (albumArray != null) { for (int i = 0; i < albumArray.length(); ++i) { JSONObject singleAlbumObject = albumArray.getJSONObject(i); Album entry = (Album) CONVERTER.convertToObject( singleAlbumObject, Album.class); albums.add(entry); } } } catch (Exception e) { e.printStackTrace(); } return albums; } /** * Get the images from recordDb given the userId and the associated albumId * * @param userId * @param albumId * @return */ public List<MediaItem> getMediaItems(String userId, String albumId) { if (userId == null) throw new InvalidParameterException("userId cannot be null"); if (albumId == null) throw new InvalidParameterException("albumId cannot be null"); List<MediaItem> mediaItems = new ArrayList<MediaItem>(); try { JSONObject recordDb = DistributedHashTable.getSingleton() .getRecord(userId); if (recordDb == null) return mediaItems; JSONArray mediaItemsArray = recordDb.optJSONArray("mediaItems"); if (mediaItemsArray != null) { for (int i = 0; i < mediaItemsArray.length(); ++i) { JSONObject singleAlbumObject = mediaItemsArray .getJSONObject(i); MediaItem entry = (MediaItem) CONVERTER.convertToObject( singleAlbumObject, MediaItem.class); if (entry.getAlbumId().equals(albumId)) mediaItems.add(entry); } } } catch (Exception e) { e.printStackTrace(); } return mediaItems; } /** * Gets the images from recordDb given the userId and the associated * albumId. Attempts to download the images from DHT. If successful, set's * the URL with the cached image link * * @param userId * @param albumId * @return */ public List<MediaItem> getMediaItemsWithCachedImages(String userId, String albumId) { if (userId == null) throw new InvalidParameterException("userId cannot be null"); if (albumId == null) throw new InvalidParameterException("albumId cannot be null"); List<MediaItem> mediaItems = this.getMediaItems(userId, albumId); for (MediaItem item : mediaItems) { String itemMagneUri = item.getUrl(); sharing.download(itemMagneUri); item.setUrl("cache/" + sharing.getHashFromMagnetURI(itemMagneUri)); } return mediaItems; } /*** * Add multiple MediaItems to an album. It updates the user's recordDb and * notifies the action in the user's Activity Stream (verb: add) * * @param userId * @param albumId * @param photos * @return */ public synchronized List<String> addMediaItemsToAlbum(String userId, String albumId, Map<File, String> photos) { if (photos == null) return null; if (userId == null) throw new InvalidParameterException("userId cannot be null"); if (albumId == null) throw new InvalidParameterException("albumId cannot be null"); Album album = null; for (Album a : this.getAlbums(userId)) { if (a.getId().equals(albumId)) { album = a; break; } } if (album == null) throw new InvalidParameterException("AlbumId " + albumId + " does not match to a valid album for the user " + userId); List<String> hashList = new ArrayList<String>(); List<MediaItem> listOfMediaItems = new ArrayList<MediaItem>(); final List<ActivityEntry> feed = activities.getFeed(userId); final String publishedDate = ISO_DATE_FORMAT.format(new Date()); try { for (Entry<File, String> mapEntry : photos.entrySet()) { File photo = mapEntry.getKey(); String mimeType = mapEntry.getValue(); String fileHash = sharing.hash(photo); final File photoCachedFile = new File(CACHE_FOLDER + File.separator + fileHash); FileUtils.copyFile(photo, photoCachedFile); photo.delete(); final String fileUrl = sharing.seed(photoCachedFile); final ActivityEntry entry = new ActivityEntryImpl(); entry.setVerb("add"); entry.setPublished(publishedDate); entry.setContent(sharing.getHashFromMagnetURI(fileUrl)); ActivityObject mediaItemObject = new ActivityObjectImpl(); mediaItemObject.setObjectType("image"); mediaItemObject.setContent(sharing .getHashFromMagnetURI(fileUrl)); mediaItemObject.setUrl(fileUrl); entry.setObject(mediaItemObject); ActivityObject mediaAlbumObject = new ActivityObjectImpl(); mediaAlbumObject.setObjectType("collection"); mediaAlbumObject.setContent(album.getTitle()); mediaAlbumObject.setId(album.getId()); entry.setTarget(mediaAlbumObject); feed.add(0, entry); MediaItem mediaItem = new MediaItemImpl(); mediaItem.setAlbumId(albumId); mediaItem.setId(sharing.getHashFromMagnetURI(fileUrl)); mediaItem.setUrl(fileUrl); mediaItem.setLastUpdated(publishedDate); mediaItem.setMimeType(mimeType); if (album.getMediaMimeType() == null) album.setMediaMimeType(new ArrayList<String>()); List<String> albumMimeTypes = album.getMediaMimeType(); if (!albumMimeTypes.contains(mimeType)) albumMimeTypes.add(mimeType); listOfMediaItems.add(mediaItem); hashList.add(sharing.getHashFromMagnetURI(fileUrl)); } album.setMediaItemCount(album.getMediaItemCount() + photos.size()); String feedUri = activities.seedActivityStream(userId, feed); // Update the album accordingly JSONObject recordDb = DistributedHashTable.getSingleton() .getRecord(userId); if (recordDb == null) recordDb = new JSONObject(); JSONArray albums = recordDb.optJSONArray("albums"); for (int i = 0; i < albums.length(); ++i) { JSONObject singleAlbumObject = albums.getJSONObject(i); Album entry1 = (Album) CONVERTER.convertToObject( singleAlbumObject, Album.class); if (entry1.getId().equals(albumId)) { albums.put(i, new JSONObject(CONVERTER.convertToString(album))); break; } } // Add all the newly created mediaItems JSONArray mediaItems = recordDb.optJSONArray("mediaItems"); if (mediaItems == null) mediaItems = new JSONArray(); for (MediaItem mediaItem : listOfMediaItems) { // Simply append new album mediaItems.put(new JSONObject(CONVERTER .convertToString(mediaItem))); } DistributedHashTable.getSingleton().store(userId, feedUri, publishedDate, albums, mediaItems); return hashList; } catch (Exception e) { e.printStackTrace(); return null; } } /*** * Adds a single MediaItem file to an Album. It updates the user's recordDb * and notifies the action in the user's Activity Stream (verb: remove) * * @param userId * @param albumId * @param photo * @param mimeType * @return */ public synchronized String addMediaItemToAlbum(String userId, String albumId, File photo, String mimeType) { Map<File, String> map = new HashMap<File, String>(); map.put(photo, mimeType); List<String> hashes = this.addMediaItemsToAlbum(userId, albumId, map); return (hashes != null && !hashes.isEmpty()) ? hashes.get(0) : null; } /*** * A Media Item is removed from an album * * @param userId * @param albumId * @param mediaId */ public synchronized void deletePhotoFromAlbum(String userId, String albumId, String mediaId) { if (mediaId == null) throw new InvalidParameterException("mediaId cannot be null"); if (userId == null) throw new InvalidParameterException("userId cannot be null"); if (albumId == null) throw new InvalidParameterException("albumId cannot be null"); Album album = null; for (Album a : this.getAlbums(userId)) { if (a.getId().equals(albumId)) { album = a; break; } } if (album == null) throw new InvalidParameterException("AlbumId " + albumId + " does not correspond to a valid album for the user " + userId); try { List<MediaItem> mediaItems = this.getMediaItems(userId, albumId); for (Iterator<MediaItem> iter = mediaItems.iterator(); iter .hasNext();) { MediaItem mediaItem = iter.next(); if (mediaId.equals(mediaItem.getId()) && albumId.equals(mediaItem.getAlbumId())) iter.remove(); } album.setMediaItemCount(mediaItems.size()); final List<ActivityEntry> feed = activities.getFeed(userId); final ActivityEntry entry = new ActivityEntryImpl(); entry.setVerb("remove"); entry.setPublished(ISO_DATE_FORMAT.format(new Date())); entry.setContent(mediaId); ActivityObject mediaItemObject = new ActivityObjectImpl(); mediaItemObject.setObjectType("image"); mediaItemObject.setContent(mediaId); entry.setObject(mediaItemObject); ActivityObject mediaAlbumObject = new ActivityObjectImpl(); mediaAlbumObject.setObjectType("collection"); mediaAlbumObject.setContent(album.getTitle()); mediaAlbumObject.setId(album.getId()); entry.setTarget(mediaAlbumObject); feed.add(0, entry); String feedUri = activities.seedActivityStream(userId, feed); JSONObject recordDb = DistributedHashTable.getSingleton() .getRecord(userId); if (recordDb == null) recordDb = new JSONObject(); JSONArray albums = recordDb.optJSONArray("albums"); // update albums if (albums != null) { for (int i = 0; i < albums.length(); ++i) { JSONObject singleAlbumObject = albums.getJSONObject(i); Album entry1 = (Album) CONVERTER.convertToObject( singleAlbumObject, Album.class); if (entry1.getId().equals(albumId)) { albums.put( i, new JSONObject(CONVERTER.convertToString(album))); break; } } } JSONArray list = new JSONArray(); JSONArray mediaItemsArray = recordDb.optJSONArray("mediaItems"); if (mediaItemsArray != null) { for (int i = 0; i < mediaItemsArray.length(); ++i) { JSONObject singleMediaItemObject = mediaItemsArray .getJSONObject(i); MediaItem entry1 = (MediaItem) CONVERTER.convertToObject( singleMediaItemObject, MediaItem.class); if (!mediaId.equals(entry1.getId()) || !albumId.equals(entry1.getAlbumId())) list.put(singleMediaItemObject); } } dht.store(userId, feedUri, entry.getPublished(), albums, list); } catch (Exception e) { e.printStackTrace(); } } }