Package com.google.livingstories.server.rpcimpl

Source Code of com.google.livingstories.server.rpcimpl.LivingStoryRpcImpl$LivingStoryAndLastUpdateTime

/**
* Copyright 2010 Google Inc.
*
* Licensed 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 com.google.livingstories.server.rpcimpl;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.livingstories.client.AssetContentItem;
import com.google.livingstories.client.AssetType;
import com.google.livingstories.client.BaseContentItem;
import com.google.livingstories.client.ContentItemType;
import com.google.livingstories.client.ContentItemTypesBundle;
import com.google.livingstories.client.LivingStory;
import com.google.livingstories.client.LivingStoryRpcService;
import com.google.livingstories.client.NarrativeContentItem;
import com.google.livingstories.client.PublishState;
import com.google.livingstories.client.StartPageBundle;
import com.google.livingstories.client.Theme;
import com.google.livingstories.client.util.DateUtil;
import com.google.livingstories.server.dataservices.LivingStoryDataService;
import com.google.livingstories.server.dataservices.ThemeDataService;
import com.google.livingstories.server.dataservices.impl.DataImplFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
* Implementation of the RPC calls related to getting data for living stories and themes.
*/
public class LivingStoryRpcImpl extends RemoteServiceServlet implements LivingStoryRpcService {
  private LivingStoryDataService livingStoryDataService;
  private ThemeDataService themeDataService;
  private ContentRpcImpl contentRpcService;
 
  public LivingStoryRpcImpl() {
    this.livingStoryDataService = DataImplFactory.getLivingStoryService();
    this.themeDataService = DataImplFactory.getThemeService();
    this.contentRpcService = new ContentRpcImpl();
  }

 
  private static final Logger logger =
      Logger.getLogger(LivingStoryRpcImpl.class.getCanonicalName());

  @Override
  public synchronized LivingStory createLivingStory(String url, String title) {
    LivingStory story = livingStoryDataService.save(null, url, title, PublishState.DRAFT, "");
    Caches.clearLivingStories();
    Caches.clearStartPageBundle();
    return story;
  }
 
  @Override
  public synchronized List<LivingStory> getAllLivingStories(boolean onlyPublished) {
    List<LivingStory> allLivingStories = Caches.getLivingStories();
    if (allLivingStories == null) {
      allLivingStories = livingStoryDataService.retrieveAll(null, true);
      Caches.setLivingStories(allLivingStories);
    }

    if (!onlyPublished) {
      return allLivingStories;
    } else {
      List<LivingStory> publishedLivingStories = Lists.newArrayList();
      for (LivingStory story : allLivingStories) {
        if (story.getPublishState() == PublishState.PUBLISHED) {
          publishedLivingStories.add(story);
        }
      }
      return publishedLivingStories;
    }
  }
 
  @Override
  public synchronized List<LivingStory> getLivingStoriesForContentManager() {
    return livingStoryDataService.retrieveAll(null, true);
  }
 
  @Override
  public LivingStory getLivingStoryById(long id, boolean allSummaryRevisions) {
    return livingStoryDataService.retrieveById(id, !allSummaryRevisions);
  }

  @Override
  public LivingStory getLivingStoryByUrl(String url) {
    return livingStoryDataService.retrieveByUrlName(url, true);
  }
 
  @Override
  public synchronized LivingStory saveLivingStory(long id, String url, String title,
      PublishState publishState, String summary) {
    LivingStory story = livingStoryDataService.save(id, url, title, publishState, summary);
    Caches.clearLivingStories();
    Caches.clearStartPageBundle();
    return story;
  }
 
  @Override
  public synchronized void deleteLivingStory(long id) {
    livingStoryDataService.delete(id);
    Caches.clearLivingStories();
    Caches.clearLivingStoryContentItems(id);
    Caches.clearLivingStoryThemes(id);
    Caches.clearLivingStoryThemeInfo(id);
    Caches.clearStartPageBundle();
  }

  @Override
  public synchronized List<Theme> getThemesForLivingStory(long livingStoryId) {
    List<Theme> themes = Caches.getLivingStoryThemes(livingStoryId);
    if (themes == null) {
      themes = themeDataService.retrieveByLivingStory(livingStoryId);
      Caches.setLivingStoryThemes(livingStoryId, themes);
    }
    return themes;
  }

  /**
   * Returns a map from theme id to ContenItemTypesBundles. It lists the published content item
   * type and asset types for the story, broken down by theme id. Each ContentItemTypeBundle
   * includes an theme name field, so this call will give client-facing code all the information
   * it needs to present themes and filters to the user.
   * @param livingStoryId living story id
   * @return a map of ContentItemTypeBundles appropriately filled in.
   */
  @Override
  public synchronized Map<Long, ContentItemTypesBundle> getThemeInfoForLivingStory(
      long livingStoryId) {
    Map<Long, ContentItemTypesBundle> result = Caches.getLivingStoryThemeInfo(livingStoryId);
    if (result != null) {
      return result;
    }

    result = Maps.newHashMap();
    ContentItemTypesBundle globalBundle = new ContentItemTypesBundle("");
    result.put(null, globalBundle);

    // put an entry in the map for each theme, too. Since some themes may have no content items,
    // we should do this up-front, not lazily.
    for (Theme theme : getThemesForLivingStory(livingStoryId)) {
      result.put(theme.getId(), new ContentItemTypesBundle(theme.getName()));
    }
   
    // In principle, we could try to track when we've found every content item type that we care
    // about, in every possible theme, terminating the loop early if our dance card is completely
    // filled, but it's such a micro-optimization at this point that it's not worthwhile.
    List<BaseContentItem> allContentItems =
        contentRpcService.getContentItemsForLivingStory(livingStoryId, true);
   
    for (BaseContentItem contentItem : allContentItems) {
      if (!addContentItemToTypesBundle(contentItem, globalBundle)) {
        continue;
      }
     
      for (Long themeId : contentItem.getThemeIds()) {
        ContentItemTypesBundle themeBundle = result.get(themeId);
        if (themeBundle == null) {
          logger.warning("contentItem " + contentItem.getId() + " refers to themeId " + themeId
              + ", but this theme does not appear to be in the story.");
        } else {
          addContentItemToTypesBundle(contentItem, themeBundle);
        }
      }
    }

    Caches.setLivingStoryThemeInfo(livingStoryId, result);
    return result;
  }     

  /**
   * Adds information on contentItem to bundle.
   * @return false if this is a content item that should be ignored completely. Handy to the caller,
   * which can then avoid adding contentItem to other, theme-specific types bundles.
   */
  private boolean addContentItemToTypesBundle(BaseContentItem contentItem,
      ContentItemTypesBundle bundle) {
    ContentItemType contentItemType = contentItem.getContentItemType();
   
    // We don't want to show background and reaction items in the filters, so skip those entirely
    if (contentItemType == ContentItemType.BACKGROUND
        || contentItemType == ContentItemType.REACTION) {
      return false;
    }
   
    if (contentItemType == ContentItemType.NARRATIVE
        && ((NarrativeContentItem)contentItem).isOpinion()) {
      bundle.opinionAvailable = true;
    } else {
      bundle.availableContentItemTypes.add(contentItemType);
      if (contentItemType == ContentItemType.ASSET) {
        AssetType assetType = ((AssetContentItem) contentItem).getAssetType();
        if (assetType == AssetType.DOCUMENT) {
          assetType = AssetType.LINK;
        }
        bundle.availableAssetTypes.add(assetType);
      }
    }
   
    return true;
  }

  @Override
  public synchronized Theme getThemeById(long id) {
    return themeDataService.retrieveById(id);
  }
 
  @Override
  public synchronized Theme saveTheme(Theme theme) {
    Theme result = themeDataService.save(theme);
    // Clear caches
    Caches.clearLivingStoryThemes(theme.getLivingStoryId());
    Caches.clearLivingStoryThemeInfo(theme.getLivingStoryId());
    return result;
  }
 
  @Override
  public synchronized void deleteTheme(long id) {
    themeDataService.delete(id);
    // Clear caches
    Caches.clearLivingStoryThemes(id);
    Caches.clearLivingStoryThemeInfo(id);
  }
 
  @Override
  public StartPageBundle getStartPageBundle() {
    StartPageBundle bundle = Caches.getStartPageBundle();
    if (bundle == null) {
      List<LivingStory> unsortedLivingStories = getAllLivingStories(true);
      Map<Long, List<BaseContentItem>> storyIdToUpdateMap =
          new HashMap<Long, List<BaseContentItem>>();
      List<LivingStoryAndLastUpdateTime> livingStoriesAndUpdateTimes =
        new ArrayList<LivingStoryAndLastUpdateTime>();
      // For each living story, get the last 3 updates - the updates are sorted in reverse
      // chronological order
      for (LivingStory livingStory : unsortedLivingStories) {
        List<BaseContentItem> updates =
            contentRpcService.getUpdatesForStartPage(livingStory.getId());
        storyIdToUpdateMap.put(livingStory.getId(), updates);
        livingStoriesAndUpdateTimes.add(
            new LivingStoryAndLastUpdateTime(livingStory,
                updates.isEmpty() ? null : updates.get(0).getTimestamp()));
      }
     
      // After we have the updates for each story, we want to sort them such that the story with
      // the latest update is first
      Collections.sort(livingStoriesAndUpdateTimes, LivingStoryAndLastUpdateTime.getComparator());
      List<LivingStory> sortedLivingStories = new ArrayList<LivingStory>();
      for (LivingStoryAndLastUpdateTime story : livingStoriesAndUpdateTimes) {
        sortedLivingStories.add(story.livingStory);
      }
      bundle = new StartPageBundle(sortedLivingStories, storyIdToUpdateMap);
      Caches.setStartPageBundle(bundle);
    }
    return bundle;
  }
 
  private static class LivingStoryAndLastUpdateTime {
    public LivingStory livingStory;
    public Date timeOfLatestUpdate;
   
    public LivingStoryAndLastUpdateTime(LivingStory livingStory, Date timeOfLatestUpdate) {
      this.livingStory = livingStory;
      this.timeOfLatestUpdate = timeOfLatestUpdate;
    }
   
    public Date timeOfLastChange() {
      return DateUtil.laterDate(livingStory.getLastChangeTimestamp(), timeOfLatestUpdate);
    }

    public static Comparator<LivingStoryAndLastUpdateTime> getComparator() {
      return new Comparator<LivingStoryAndLastUpdateTime>() {
        @Override
        public int compare(LivingStoryAndLastUpdateTime lhs, LivingStoryAndLastUpdateTime rhs) {
          Date lhsTime = lhs.timeOfLastChange();
          Date rhsTime = rhs.timeOfLastChange();
          // Sort stories with no updates to the end.
          if (rhsTime == null && lhsTime == null) {
            return 0;
          } else if (rhsTime == null) {
            return 1;
          } else if (lhsTime == null) {
            return -1;
          } else {
            return rhsTime.compareTo(lhsTime);
          }
        }
      };
    }
  }
}
TOP

Related Classes of com.google.livingstories.server.rpcimpl.LivingStoryRpcImpl$LivingStoryAndLastUpdateTime

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.