Package org.rssowl.core.internal.persist.service

Source Code of org.rssowl.core.internal.persist.service.DBHelper

/*   **********************************************************************  **
**   Copyright notice                                                       **
**                                                                          **
**   (c) 2005-2009 RSSOwl Development Team                                  **
**   http://www.rssowl.org/                                                 **
**                                                                          **
**   All rights reserved                                                    **
**                                                                          **
**   This program and the accompanying materials are made available under   **
**   the terms of the Eclipse Public License v1.0 which accompanies this    **
**   distribution, and is available at:                                     **
**   http://www.rssowl.org/legal/epl-v10.html                               **
**                                                                          **
**   A copy is found in the file epl-v10.html and important notices to the  **
**   license from the team is found in the textfile LICENSE.txt distributed **
**   in this package.                                                       **
**                                                                          **
**   This copyright notice MUST APPEAR in all copies of the file!           **
**                                                                          **
**   Contributors:                                                          **
**     RSSOwl Development Team - initial API and implementation             **
**                                                                          **
**  **********************************************************************  */

package org.rssowl.core.internal.persist.service;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.rssowl.core.Owl;
import org.rssowl.core.internal.Activator;
import org.rssowl.core.internal.InternalOwl;
import org.rssowl.core.internal.persist.BookMark;
import org.rssowl.core.internal.persist.Description;
import org.rssowl.core.internal.persist.Feed;
import org.rssowl.core.internal.persist.LazyList;
import org.rssowl.core.internal.persist.News;
import org.rssowl.core.internal.persist.dao.DAOServiceImpl;
import org.rssowl.core.internal.persist.dao.EntitiesToBeIndexedDAOImpl;
import org.rssowl.core.internal.persist.dao.IDescriptionDAO;
import org.rssowl.core.persist.IBookMark;
import org.rssowl.core.persist.IEntity;
import org.rssowl.core.persist.IFeed;
import org.rssowl.core.persist.INews;
import org.rssowl.core.persist.INewsBin;
import org.rssowl.core.persist.IPersistable;
import org.rssowl.core.persist.NewsCounter;
import org.rssowl.core.persist.INewsBin.StatesUpdateInfo;
import org.rssowl.core.persist.dao.DAOService;
import org.rssowl.core.persist.dao.DynamicDAO;
import org.rssowl.core.persist.dao.INewsBinDAO;
import org.rssowl.core.persist.dao.INewsCounterDAO;
import org.rssowl.core.persist.event.FeedEvent;
import org.rssowl.core.persist.event.ModelEvent;
import org.rssowl.core.persist.event.NewsBinEvent;
import org.rssowl.core.persist.event.NewsEvent;
import org.rssowl.core.persist.event.runnable.EventRunnable;
import org.rssowl.core.persist.event.runnable.FeedEventRunnable;
import org.rssowl.core.persist.event.runnable.NewsEventRunnable;
import org.rssowl.core.persist.reference.FeedLinkReference;
import org.rssowl.core.persist.reference.NewsReference;
import org.rssowl.core.persist.service.PersistenceException;
import org.rssowl.core.persist.service.UniqueConstraintException;

import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.ext.Db4oException;
import com.db4o.query.Query;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Helper for DB related tasks.
*/
public final class DBHelper {
  static final int BUFFER = 32768;

  private DBHelper() {
    super();
  }

  public static void rename(File origin, File destination) throws PersistenceException {

    /* Try atomic rename first. If that fails, rely on delete + rename */
    if (!origin.renameTo(destination)) {
      destination.delete();
      if (!origin.renameTo(destination)) {
        throw new PersistenceException("Failed to rename: " + origin + " to: " + destination); //$NON-NLS-1$ //$NON-NLS-2$
      }
    }
  }

  public static String readFirstLineFromFile(File file) {
    BufferedReader reader = null;
    try {
      try {
        reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); //$NON-NLS-1$
      } catch (UnsupportedEncodingException e) {
        reader = new BufferedReader(new FileReader(file));
      }
      String text = reader.readLine();
      return text;
    } catch (IOException e) {
      throw new PersistenceException(e);
    } finally {
      DBHelper.closeQuietly(reader);
    }
  }

  public static final void copyFileNIO(File originFile, File destinationFile) {
    FileInputStream inputStream = null;
    FileOutputStream outputStream = null;
    try {
      inputStream = new FileInputStream(originFile);
      FileChannel srcChannel = inputStream.getChannel();

      if (!destinationFile.exists())
        destinationFile.createNewFile();

      outputStream = new FileOutputStream(destinationFile);
      FileChannel dstChannel = outputStream.getChannel();

      long bytesToTransfer = srcChannel.size();
      long position = 0;
      while (bytesToTransfer > 0) {
        long bytesTransferred = dstChannel.transferFrom(srcChannel, position, bytesToTransfer);
        position += bytesTransferred;
        bytesToTransfer -= bytesTransferred;
      }

    } catch (IOException e) {
      Activator.getDefault().logError("Failed to copy file using NIO. Falling back to traditional IO", e); //$NON-NLS-1$
      copyFileIO(originFile, destinationFile, new NullProgressMonitor());
    } finally {
      closeQuietly(inputStream);
      closeQuietly(outputStream);
    }
  }

  public static void copyFileIO(File originFile, File destinationFile, IProgressMonitor monitor) {
    FileInputStream inputStream = null;
    FileOutputStream outputStream = null;
    try {
      inputStream = new FileInputStream(originFile);

      if (!destinationFile.exists())
        destinationFile.createNewFile();
      outputStream = new FileOutputStream(destinationFile);

      int i = 0;
      byte[] buf = new byte[BUFFER];
      while ((i = inputStream.read(buf)) != -1 && !monitor.isCanceled()) {
        outputStream.write(buf, 0, i);
        monitor.worked(1);
      }
    } catch (IOException e) {
      throw new PersistenceException(e);
    } finally {
      closeQuietly(inputStream);
      closeQuietly(outputStream);
    }
  }

  public static void writeToFile(File file, String text) {
    BufferedWriter writer = null;
    try {
      try {
        writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8")); //$NON-NLS-1$
      } catch (UnsupportedEncodingException e) {
        writer = new BufferedWriter(new FileWriter(file));
      }
      writer.write(text);
      writer.flush();
    } catch (IOException e) {
      throw new PersistenceException(e);
    } finally {
      closeQuietly(writer);
    }
  }

  public static void closeQuietly(Closeable closeable) {
    if (closeable != null)
      try {
        closeable.close();
      } catch (IOException e) {
        Activator.getDefault().logError("Failed to close stream.", e); //$NON-NLS-1$
      }
  }

  public static final List<EventRunnable<?>> cleanUpEvents() {
    List<EventRunnable<?>> eventNotifiers = EventsMap.getInstance().removeEventRunnables();
    EventsMap.getInstance().removeEventTemplatesMap();
    EventManager.getInstance().clear();
    return eventNotifiers;
  }

  public static final void cleanUpAndFireEvents() {
    fireEvents(cleanUpEvents());
  }

  public static final void fireEvents(List<EventRunnable<?>> eventNotifiers) {
    if (eventNotifiers == null) {
      return;
    }
    for (EventRunnable<?> runnable : eventNotifiers) {
      runnable.run();
    }
  }

  public static final PersistenceException rollbackAndPE(ObjectContainer db, Exception e) {
    DBHelper.cleanUpEvents();
    db.rollback();
    return new PersistenceException(e);
  }

  public static final void putEventTemplate(ModelEvent modelEvent) {
    EventsMap.getInstance().putEventTemplate(modelEvent);
  }

  public static final void saveFeed(ObjectContainer db, IFeed feed) {
    if (feed.getId() == null && feedExists(db, feed.getLink()))
      throw new UniqueConstraintException("link", feed); //$NON-NLS-1$

    ModelEvent feedEventTemplate = new FeedEvent(feed, true);
    DBHelper.putEventTemplate(feedEventTemplate);
    saveAndCascadeAllNews(db, feed.getNews(), false);
    saveEntities(db, feed.getCategories());
    saveEntity(db, feed.getAuthor());
    saveEntity(db, feed.getImage());

    db.ext().store(feed, 2);
  }

  private static void saveEntity(ObjectContainer db, IPersistable entity) {
    if (entity != null)
      db.store(entity);
  }

  private static void saveEntities(ObjectContainer db, List<? extends IEntity> entities) {
    for (IEntity entity : entities)
      db.ext().store(entity, 1);
  }

  static void saveAndCascadeAllNews(ObjectContainer db, Collection<INews> newsCollection, boolean root) {
    for (INews news : newsCollection)
      ((News) news).acquireReadLockSpecial();

    try {
      for (INews news : newsCollection)
        saveAndCascadeNews(db, news, root);
    } finally {
      for (INews news : newsCollection) {
        News n = (News) news;
        n.releaseReadLockSpecial();
        n.clearTransientDescription();
      }
    }
  }

  public static final INews peekPersistedNews(ObjectContainer db, INews news) {
    INews oldNews = db.ext().peekPersisted(news, 3, true);
    if (oldNews instanceof News) {
      ((News) oldNews).init();
    }
    return oldNews;
  }

  public static final void saveNews(ObjectContainer db, INews news) {
    saveNews(db, news, 2);
  }

  public static final void saveNews(ObjectContainer db, INews news, Integer updateDepth) {
    INews oldNews = peekPersistedNews(db, news);
    if (oldNews != null) {
      ModelEvent newsEventTemplate = new NewsEvent(oldNews, news, false);
      DBHelper.putEventTemplate(newsEventTemplate);
    }
    db.ext().store(news, updateDepth);
  }

  static final boolean feedExists(ObjectContainer db, URI link) {
    return !getFeeds(db, link).isEmpty();
  }

  @SuppressWarnings("unchecked")
  private static List<Feed> getFeeds(ObjectContainer db, URI link) {
    Query query = db.query();
    query.constrain(Feed.class);
    query.descend("fLinkText").constrain(link.toString()); //$NON-NLS-1$
    List<Feed> set = query.execute();
    return set;
  }

  public static final Feed loadFeed(ObjectContainer db, URI link, Integer activationDepth) {
    try {
      List<Feed> feeds = getFeeds(db, link);
      if (!feeds.isEmpty()) {
        Feed feed = feeds.iterator().next();
        if (activationDepth != null)
          db.ext().activate(feed, activationDepth.intValue());

        return feed;
      }
      return null;
    } catch (Db4oException e) {
      throw new PersistenceException(e);
    }
  }

  public static final void saveAndCascadeNews(ObjectContainer db, INews news, boolean root) {
    INews oldNews = peekPersistedNews(db, news);
    if (oldNews != null || root) {
      ModelEvent event = new NewsEvent(oldNews, news, root);
      putEventTemplate(event);
    }
    saveEntities(db, news.getCategories());
    saveEntity(db, news.getAuthor());
    saveEntities(db, news.getAttachments());
    saveEntity(db, news.getSource());
    db.ext().store(news, 2);
    saveDescription(db, news);
  }

  private static void saveDescription(ObjectContainer db, INews news) {
    News n = (News) news;

    /*
     * Avoid loading from the db if the description of the news being saved has
     * not been changed.
     */
    if (!n.isTransientDescriptionSet())
      return;

    Description dbDescription = null;
    String dbDescriptionValue = null;

    dbDescription = getDescriptionDAO().load(news.getId());
    if (dbDescription != null)
      dbDescriptionValue = dbDescription.getValue();

    String newsDescriptionValue = n.getTransientDescription();

    /*
     * If the description in the news has been set to null and it's already null
     * in the database, there is nothing to do.
     */
    if (dbDescriptionValue == null && newsDescriptionValue == null)
      return;

    else if (dbDescriptionValue == null && newsDescriptionValue != null)
      db.store(new Description(news, newsDescriptionValue));

    else if (dbDescriptionValue != null && newsDescriptionValue == null)
      db.delete(dbDescription);

    else if (dbDescriptionValue != null && !dbDescriptionValue.equals(newsDescriptionValue)) {
      if (dbDescription != null) {
        dbDescription.setDescription(newsDescriptionValue);
        db.store(dbDescription);
      }
    }
  }

  public static IDescriptionDAO getDescriptionDAO() {
    DAOService daoService = InternalOwl.getDefault().getPersistenceService().getDAOService();
    if (daoService instanceof DAOServiceImpl)
      return ((DAOServiceImpl) daoService).getDescriptionDAO();

    throw new IllegalStateException("This method should only be called if DAOService is of type " + DAOServiceImpl.class + ", but it is of type: " + daoService.getClass()); //$NON-NLS-1$ //$NON-NLS-2$
  }

  public static void preCommit(ObjectContainer db) {
    updateNewsCounter(db);
    updateNewsToBeIndexed(db);
    updateNewsBins(db);
  }

  public static EntitiesToBeIndexedDAOImpl getEntitiesToBeIndexedDAO() {
    DAOService service = InternalOwl.getDefault().getPersistenceService().getDAOService();
    if (service instanceof DAOServiceImpl) {
      EntitiesToBeIndexedDAOImpl entitiesToBeIndexedDAO = ((DAOServiceImpl) service).getEntitiesToBeIndexedDAO();
      return entitiesToBeIndexedDAO;
    }
    return null;
  }

  private static void updateNewsToBeIndexed(ObjectContainer db) {
    NewsEventRunnable newsEventRunnables = getNewsEventRunnables(EventsMap.getInstance().getEventRunnables());
    if (newsEventRunnables == null)
      return;

    EntitiesToBeIndexedDAOImpl dao = getEntitiesToBeIndexedDAO();
    EntityIdsByEventType newsToBeIndexed = dao.load();
    Set<NewsEvent> updateEvents = new HashSet<NewsEvent>(newsEventRunnables.getUpdateEvents().size());
    Set<NewsEvent> deleteEvents = new HashSet<NewsEvent>(newsEventRunnables.getRemoveEvents());
    Set<NewsEvent> persistEvents = filterPersistedNewsForIndexing(newsEventRunnables.getPersistEvents());
    for (NewsEvent event : newsEventRunnables.getUpdateEvents())
      indexTypeForNewsUpdate(event, persistEvents, updateEvents, deleteEvents);

    NewsEventRunnable copy = new NewsEventRunnable();
    for (NewsEvent persistEvent : persistEvents)
      copy.addCheckedPersistEvent(persistEvent);
    for (NewsEvent updateEvent : updateEvents)
      copy.addCheckedUpdateEvent(updateEvent);
    for (NewsEvent deleteEvent : deleteEvents)
      copy.addCheckedRemoveEvent(deleteEvent);

    newsToBeIndexed.addAllEntities(copy.getPersistEvents(), copy.getUpdateEvents(), copy.getRemoveEvents());
    newsToBeIndexed.compact();
    db.ext().store(newsToBeIndexed, Integer.MAX_VALUE);
  }

  public static Set<NewsEvent> filterPersistedNewsForIndexing(Collection<NewsEvent> events) {
    Set<NewsEvent> result = new HashSet<NewsEvent>(events.size());
    for (NewsEvent event : events)
      if (event.getEntity().isVisible())
        result.add(event);
    return result;
  }

  public static void indexTypeForNewsUpdate(NewsEvent event, Collection<NewsEvent> newsToRestore, Collection<NewsEvent> newsToUpdate, Collection<NewsEvent> newsToDelete) {
    boolean wasVisible = event.getOldNews().isVisible();
    boolean isVisible = event.getEntity().isVisible();

    /* News got Deleted/Hidden */
    if (wasVisible && !isVisible)
      newsToDelete.add(event);

    /* News got Restored */
    else if (!wasVisible && isVisible)
      newsToRestore.add(event);

    /* Normal Update */
    else if (wasVisible && isVisible)
      newsToUpdate.add(event);
  }

  public static NewsEventRunnable getNewsEventRunnables(List<EventRunnable<?>> eventRunnables) {
    for (EventRunnable<?> eventRunnable : eventRunnables) {
      if (eventRunnable instanceof NewsEventRunnable)
        return (NewsEventRunnable) eventRunnable;
    }
    return null;
  }

  private static void updateNewsBins(ObjectContainer db) {
    NewsEventRunnable newsEventRunnable = getNewsEventRunnables(EventsMap.getInstance().getEventRunnables());

    if (newsEventRunnable == null)
      return;

    Map<Long, List<StatesUpdateInfo>> statesUpdateInfos = new HashMap<Long, List<StatesUpdateInfo>>(5);
    for (NewsEvent newsEvent : newsEventRunnable.getUpdateEvents()) {
      INews news = newsEvent.getEntity();
      if (news.getParentId() != 0 && (newsEvent.getOldNews().getState() != news.getState())) {
        List<StatesUpdateInfo> list = statesUpdateInfos.get(news.getParentId());
        if (list == null) {
          list = new ArrayList<StatesUpdateInfo>();
          statesUpdateInfos.put(news.getParentId(), list);
        }
        list.add(new StatesUpdateInfo(newsEvent.getOldNews().getState(), news.getState(), news.toReference()));
      }
    }
    for (NewsEvent newsEvent : newsEventRunnable.getPersistEvents()) {
      INews news = newsEvent.getEntity();
      if (news.getParentId() != 0) {
        List<StatesUpdateInfo> list = statesUpdateInfos.get(news.getParentId());
        if (list == null) {
          list = new ArrayList<StatesUpdateInfo>();
          statesUpdateInfos.put(news.getParentId(), list);
        }
        list.add(new StatesUpdateInfo(null, news.getState(), news.toReference()));
      }
    }
    if (!statesUpdateInfos.isEmpty()) {
      Set<FeedLinkReference> removedFeedRefs = new HashSet<FeedLinkReference>();
      INewsBinDAO newsBinDAO = DynamicDAO.getDAO(INewsBinDAO.class);
      for (Map.Entry<Long, List<StatesUpdateInfo>> mapEntry : statesUpdateInfos.entrySet()) {
        INewsBin newsBin = newsBinDAO.load(mapEntry.getKey());
        if (newsBin.updateNewsStates(mapEntry.getValue())) {
          removeNews(db, removedFeedRefs, newsBin.removeNews(EnumSet.of(INews.State.DELETED)));
          putEventTemplate(new NewsBinEvent(newsBin, null, true));
          db.ext().store(newsBin, Integer.MAX_VALUE);
        }
      }
      removeFeedsAfterNewsBinUpdate(db, removedFeedRefs);
    }
  }

  static void removeNews(ObjectContainer db, Set<FeedLinkReference> feedRefs, Collection<NewsReference> newsRefs) {
    for (NewsReference newsRef : newsRefs) {
      INews news = newsRef.resolve();
      if (news != null) {
        feedRefs.add(news.getFeedReference());
        db.delete(news);
      }
    }
  }

  static void removeFeedsAfterNewsBinUpdate(ObjectContainer db, Set<FeedLinkReference> removedFeedRefs) {

    NewsCounter newsCounter = DynamicDAO.getDAO(INewsCounterDAO.class).load();
    boolean changed = false;
    for (FeedLinkReference feedRef : removedFeedRefs) {
      if ((countBookMarkReference(db, feedRef) == 0) && !feedHasNewsWithCopies(db, feedRef)) {
        db.delete(feedRef.resolve());
        changed = true;
      }
    }
    if (changed)
      db.ext().store(newsCounter, Integer.MAX_VALUE);
  }

  static int countBookMarkReference(ObjectContainer db, FeedLinkReference feedRef) {
    Collection<IBookMark> marks = loadAllBookMarks(db, feedRef);
    return marks.size();
  }

  @SuppressWarnings("unchecked")
  public static Collection<IBookMark> loadAllBookMarks(ObjectContainer db, FeedLinkReference feedRef) {
    Query query = db.query();
    query.constrain(BookMark.class);
    query.descend("fFeedLink").constrain(feedRef.getLink().toString()); //$NON-NLS-1$
    return query.execute();
  }

  static boolean feedHasNewsWithCopies(ObjectContainer db, FeedLinkReference feedRef) {
    Query query = db.query();
    query.constrain(News.class);
    query.descend("fFeedLink").constrain(feedRef.getLink().toString()); //$NON-NLS-1$
    query.descend("fParentId").constrain(0).not(); //$NON-NLS-1$
    return !query.execute().isEmpty();
  }

  public static void updateNewsCounter(ObjectContainer db) {
    List<EventRunnable<?>> eventRunnables = EventsMap.getInstance().getEventRunnables();
    NewsCounterService newsCounterService = new NewsCounterService(Owl.getPersistenceService().getDAOService().getNewsCounterDAO(), db);
    NewsEventRunnable newsEventRunnable = getNewsEventRunnables(eventRunnables);
    if (newsEventRunnable != null) {
      newsCounterService.onNewsAdded(newsEventRunnable.getPersistEvents());
      newsCounterService.onNewsRemoved((newsEventRunnable.getRemoveEvents()));
      newsCounterService.onNewsUpdated(newsEventRunnable.getUpdateEvents());
    }
    for (EventRunnable<?> eventRunnable : eventRunnables) {
      if (eventRunnable instanceof FeedEventRunnable) {
        FeedEventRunnable feedEventRunnable = (FeedEventRunnable) eventRunnable;
        newsCounterService.onFeedRemoved(feedEventRunnable.getRemoveEvents());
        break;
      }
    }
  }

  public static Collection<IFeed> loadAllFeeds(ObjectContainer db) {
    ObjectSet<? extends IFeed> entities = db.query(Feed.class);
    return new LazyList<IFeed>(entities, db);
  }
}
TOP

Related Classes of org.rssowl.core.internal.persist.service.DBHelper

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.