org.apache.lucene.gdata.storage.db4o.DB4oStorage.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.lucene.gdata.storage.db4o.DB4oStorage.java

Source

/**
 * 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.lucene.gdata.storage.db4o;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.data.GDataAccount;
import org.apache.lucene.gdata.data.ServerBaseEntry;
import org.apache.lucene.gdata.data.ServerBaseFeed;
import org.apache.lucene.gdata.storage.ModificationConflictException;
import org.apache.lucene.gdata.storage.ResourceNotFoundException;
import org.apache.lucene.gdata.storage.Storage;
import org.apache.lucene.gdata.storage.StorageController;
import org.apache.lucene.gdata.storage.StorageException;

import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.query.Query;
import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.DateTime;

/**
 * 
 * Storage implementation for the DB4o storage component
 * @author Simon Willnauer
 * 
 */
public class DB4oStorage implements Storage {
    private static final Log LOG = LogFactory.getLog(DB4oStorage.class);

    private static final int RENDER_ACTIVATION_DEPTH = 100;

    private final ObjectContainer container;

    private final StorageController controller;

    private final List<String> semaphore = new ArrayList<String>();

    protected DB4oStorage(final ObjectContainer container, StorageController controller) {
        this.container = container;
        this.controller = controller;
    }

    private void createSemaphore(String key) throws ModificationConflictException {
        this.semaphore.add(key);
        if (this.container.ext().setSemaphore(key, 0))
            return;
        throw new ModificationConflictException("can not create semaphore for key -- " + key);
    }

    private void releaseAllSemaphore() {
        for (String key : this.semaphore) {
            this.container.ext().releaseSemaphore(key);
        }
        this.semaphore.clear();
    }

    private void releaseSemaphore(String key) {
        if (this.semaphore.contains(key)) {
            this.container.ext().releaseSemaphore(key);
            this.semaphore.remove(key);
        }
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#storeEntry(org.apache.lucene.gdata.data.ServerBaseEntry)
     */
    public BaseEntry storeEntry(ServerBaseEntry entry) throws StorageException {
        if (entry == null)
            throw new StorageException("Can not store entry -- is null");

        if (entry.getFeedId() == null)
            throw new StorageException("can not store entry -- feed id is null");
        if (LOG.isDebugEnabled())
            LOG.debug("Storing entry for feed: " + entry.getFeedId());
        BaseFeed<BaseFeed, BaseEntry> feed = getFeedOnly(entry.getFeedId(), entry.getServiceType());
        refreshPersistentObject(feed);
        try {
            StringBuilder idBuilder = new StringBuilder(entry.getFeedId());
            idBuilder.append(this.controller.releaseId());
            entry.setId(idBuilder.toString());
        } catch (StorageException e) {
            LOG.error("Can not create uid for entry -- " + e.getMessage(), e);
            throw new StorageException("Can not create uid for entry -- " + e.getMessage(), e);

        }
        setUpdated(entry, feed);
        DB4oEntry intEntry = new DB4oEntry();
        intEntry.setEntry(entry.getEntry());
        intEntry.setUpdateTime(entry.getUpdated().getValue());
        intEntry.setFeedId(feed.getId());
        intEntry.setVersion(entry.getVersion());

        try {
            this.container.set(feed);
            this.container.set(intEntry);
            this.container.commit();
        } catch (Exception e) {
            LOG.error("Error occured on persisting changes -- rollback changes");
            this.container.rollback();
            throw new StorageException("Can not persist changes -- " + e.getMessage(), e);
        }
        if (LOG.isInfoEnabled())
            LOG.info("Stored Entry for entryID: " + entry.getId() + " -- feedID: " + entry.getFeedId());
        return entry.getEntry();
    }

    private void setUpdated(ServerBaseEntry entry, DB4oEntry intEntry) {
        if (entry.getUpdated().compareTo(intEntry.getEntry().getUpdated()) <= 0) {
            if (LOG.isDebugEnabled())
                LOG.debug(
                        "Set new UpdateTime to entry new entry time is less or equal the time of the stored entry -- old Entry: "
                                + intEntry.getEntry().getUpdated() + "; new Entry: " + entry.getUpdated());
            entry.setUpdated(new DateTime(System.currentTimeMillis(), entry.getUpdated().getTzShift()));
        }

    }

    private void setUpdated(ServerBaseEntry entry, BaseFeed<BaseFeed, BaseEntry> feed) {
        if (entry.getUpdated() != null) {
            long timeInMilli = entry.getUpdated().getValue();
            int tzShift = entry.getUpdated().getTzShift();
            feed.setUpdated(new DateTime(timeInMilli, tzShift));
        } else {
            int timezone = 0;
            if (feed.getUpdated() != null) {
                timezone = feed.getUpdated().getTzShift();
            }
            long timeInMilli = System.currentTimeMillis();
            entry.setUpdated(new DateTime(timeInMilli, timezone));
            feed.setUpdated(new DateTime(timeInMilli, timezone));
        }
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#deleteEntry(org.apache.lucene.gdata.data.ServerBaseEntry)
     */
    public void deleteEntry(ServerBaseEntry entry) throws StorageException {
        if (entry == null)
            throw new StorageException("Can not delete entry -- is null");
        if (entry.getFeedId() == null)
            throw new StorageException("can not delete entry -- feed id is null");
        if (entry.getId() == null)
            throw new StorageException("Can not delete entry -- id is null");
        if (LOG.isDebugEnabled())
            LOG.debug("delete entry for feed: " + entry.getFeedId() + " entry ID: " + entry.getId());
        DB4oEntry persistentEntry = getInternalEntry(entry.getId());
        // lock the entry to prevent concurrent access
        createSemaphore(entry.getId());
        refreshPersistentObject(persistentEntry);
        if (persistentEntry.getVersion() != entry.getVersion())
            throw new ModificationConflictException(
                    "Current version does not match given version  -- currentVersion: "
                            + persistentEntry.getVersion() + "; given Version: " + entry.getVersion());
        BaseFeed<BaseFeed, BaseEntry> feed = getFeedOnly(entry.getFeedId(), entry.getServiceType());
        refreshPersistentObject(feed);
        DateTime time = DateTime.now();
        if (persistentEntry.getEntry().getUpdated() != null)
            time.setTzShift(persistentEntry.getEntry().getUpdated().getTzShift());
        feed.setUpdated(time);
        try {
            //delete the entry
            this.container.delete(persistentEntry.getEntry());
            this.container.delete(persistentEntry);
            this.container.set(feed);
            this.container.commit();

        } catch (Exception e) {
            LOG.error("Error occured on persisting changes -- rollback changes");
            this.container.rollback();
            throw new StorageException("Can not persist changes -- " + e.getMessage(), e);
        } finally {
            releaseSemaphore(entry.getId());
        }
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#updateEntry(org.apache.lucene.gdata.data.ServerBaseEntry)
     */
    public BaseEntry updateEntry(ServerBaseEntry entry) throws StorageException {
        if (entry == null)
            throw new StorageException("Can not update entry -- is null");
        if (entry.getFeedId() == null)
            throw new StorageException("can not delete entry -- feed id is null");
        if (entry.getId() == null)
            throw new StorageException("Can not delete entry -- id is null");

        DB4oEntry persistentEntry = getInternalEntry(entry.getId());
        // lock the entry to prevent concurrent access
        createSemaphore(entry.getId());
        refreshPersistentObject(persistentEntry);
        if (persistentEntry.getVersion() != entry.getVersion())
            throw new ModificationConflictException(
                    "Current version does not match given version  -- currentVersion: "
                            + persistentEntry.getVersion() + "; given Version: " + entry.getVersion());

        setUpdated(entry, persistentEntry);
        BaseFeed<BaseFeed, BaseEntry> feed = getFeedOnly(entry.getFeedId(), entry.getServiceType());
        refreshPersistentObject(feed);
        BaseEntry retVal = entry.getEntry();
        DB4oEntry newEntry = new DB4oEntry();
        newEntry.setEntry(retVal);
        newEntry.setUpdateTime(entry.getUpdated().getValue());
        newEntry.setFeedId(feed.getId());
        // increment Version
        newEntry.setVersion((entry.getVersion()) + 1);

        setUpdated(entry, feed);
        try {
            this.container.set(feed);
            this.container.set(newEntry);
            this.container.delete(persistentEntry.getEntry());
            this.container.delete(persistentEntry);
            this.container.commit();
        } catch (Exception e) {
            LOG.error("Error occured on persisting changes -- rollback changes");
            this.container.rollback();
            throw new StorageException("Can not persist changes -- " + e.getMessage(), e);
        } finally {
            releaseSemaphore(entry.getId());
        }
        return retVal;

    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#getFeed(org.apache.lucene.gdata.data.ServerBaseFeed)
     */
    @SuppressWarnings("unchecked")
    public BaseFeed getFeed(ServerBaseFeed feed) throws StorageException {
        if (feed.getId() == null)
            throw new StorageException("can not get feed -- feed id is null");
        if (feed.getStartIndex() < 1)
            feed.setStartIndex(1);
        if (feed.getItemsPerPage() < 0)
            feed.setItemsPerPage(25);

        if (LOG.isInfoEnabled())
            LOG.info("Fetching feed for feedID: " + feed.getId() + "; start-index: " + feed.getStartIndex()
                    + "; items per page: " + feed.getItemsPerPage());

        BaseFeed<BaseFeed, BaseEntry> persistentFeed = getFeedOnly(feed.getId(), feed.getServiceType());
        /*
         * prevent previously added entries in long running storage instances
         */
        clearDynamicElements(persistentFeed);
        Query query = this.container.query();
        query.constrain(DB4oEntry.class);
        query.descend("feedId").constrain(feed.getId()).equal();
        query.descend("updateTime").orderDescending();

        ObjectSet<DB4oEntry> set = query.execute();

        int size = set.size();

        if (size < feed.getStartIndex()) {
            if (LOG.isDebugEnabled())
                LOG.debug("no entries found for feed constrain -- feedID: " + feed.getId() + "; start-index: "
                        + feed.getStartIndex() + "; items per page: " + feed.getItemsPerPage());
            return persistentFeed;
        }

        int start = feed.getStartIndex() - 1;
        int items = start + feed.getItemsPerPage();
        if (items > size)
            items = size;

        List<DB4oEntry> sublist = set.subList(start, items);

        for (DB4oEntry entry : sublist) {
            persistentFeed.getEntries().add(clearDynamicElements(entry.getEntry()));
        }
        this.container.activate(persistentFeed, RENDER_ACTIVATION_DEPTH);
        return persistentFeed;

    }

    @SuppressWarnings("unchecked")
    private BaseFeed<BaseFeed, BaseEntry> getFeedOnly(final String feedId, final String serviceId)
            throws StorageException {
        if (!checkService(feedId, serviceId))
            throw new StorageException();
        Query query = this.container.query();
        query.constrain(ServerBaseFeed.class);

        query.constrain(BaseFeed.class);

        query.descend("id").constrain(feedId).equal();

        ObjectSet set = query.execute();
        if (set.size() > 1)
            throw new StorageException("Query for feed id " + feedId + " returns more than one result");
        if (set.hasNext())
            return (BaseFeed<BaseFeed, BaseEntry>) set.next();
        throw new ResourceNotFoundException("can not find feed for given feed id -- " + feedId);

    }

    private boolean checkService(String feedId, String serviceId) {
        Query query = this.container.query();
        query.constrain(ServerBaseFeed.class);
        query.descend("feed").descend("id").constrain(feedId).equal();
        query.descend("serviceType").constrain(serviceId).equal();
        return query.execute().size() == 1;
    }

    private ObjectSet getEnriesForFeedID(String feedId) {
        Query query = this.container.query();
        query.constrain(DB4oEntry.class);
        query.descend("feedId").constrain(feedId).equal();

        return query.execute();
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#getEntry(org.apache.lucene.gdata.data.ServerBaseEntry)
     */

    public BaseEntry getEntry(ServerBaseEntry entry) throws StorageException {
        if (entry == null)
            throw new StorageException("can not retrieve entry -- is null");
        if (entry.getId() == null)
            throw new StorageException("can not retrieve entry -- id is null");
        if (LOG.isInfoEnabled())
            LOG.info("Retrieving entry for entryID: " + entry.getId());
        DB4oEntry retval = getInternalEntry(entry.getId());
        this.container.activate(retval.getEntry(), RENDER_ACTIVATION_DEPTH);
        return clearDynamicElements(retval.getEntry());
    }

    @SuppressWarnings("unchecked")
    private DB4oEntry getInternalEntry(String id) throws StorageException {
        Query query = this.container.query();
        query.constrain(DB4oEntry.class);
        query.descend("entry").descend("id").constrain(id).equal();
        ObjectSet<DB4oEntry> resultSet = query.execute();
        if (resultSet.size() > 1)
            throw new StorageException("Entry query returned not a unique result");
        if (resultSet.hasNext())
            return resultSet.next();
        throw new ResourceNotFoundException("no entry with entryID: " + id + " stored -- query returned no result");
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#storeAccount(org.apache.lucene.gdata.data.GDataAccount)
     */
    public void storeAccount(GDataAccount account) throws StorageException {
        if (account == null)
            throw new StorageException("can not store account -- is null");
        if (account.getName() == null)
            throw new StorageException("can not store account -- name is null");
        if (account.getPassword() == null)
            throw new StorageException("can not store account -- password is null");
        try {
            getAccount(account.getName());
            throw new IllegalStateException("account with accountname: " + account.getName() + " already exists");
        } catch (IllegalStateException e) {
            throw new StorageException("Account already exists");
        } catch (StorageException e) {
            if (LOG.isDebugEnabled())
                LOG.debug("checked account for existence -- does not exist -- store account");
        }
        try {
            this.container.set(account);
            this.container.commit();
        } catch (Exception e) {
            LOG.error("Error occured on persisting changes -- rollback changes");
            this.container.rollback();
            throw new StorageException("Can not persist changes -- " + e.getMessage(), e);
        }
        if (LOG.isInfoEnabled())
            LOG.info("Stored account: " + account);
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#updateAccount(org.apache.lucene.gdata.data.GDataAccount)
     */
    public void updateAccount(GDataAccount account) throws StorageException {
        if (account == null)
            throw new StorageException("can not update account -- is null");
        if (account.getName() == null)
            throw new StorageException("can not update account -- name is null");
        if (account.getPassword() == null)
            throw new StorageException("can not update account -- password is null");
        GDataAccount persitentAccount = getAccount(account.getName());
        refreshPersistentObject(persitentAccount);
        try {
            this.container.set(account);
            this.container.delete(persitentAccount);
            this.container.commit();
        } catch (Exception e) {
            LOG.error("Error occured on persisting changes -- rollback changes");
            this.container.rollback();
            throw new StorageException("Can not persist changes -- " + e.getMessage(), e);
        }
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#deleteAccount(java.lang.String)
     */
    public void deleteAccount(String accountname) throws StorageException {
        if (accountname == null)
            throw new StorageException("can not delete account -- account name is null");
        GDataAccount account = this.getAccount(accountname);
        refreshPersistentObject(account);
        if (LOG.isInfoEnabled())
            LOG.info("delete account -- account name: " + accountname);
        try {
            this.container.delete(account);
            this.container.commit();
        } catch (Exception e) {
            LOG.error("Error occured on persisting changes -- rollback changes");
            this.container.rollback();
            throw new StorageException("Can not persist changes -- " + e.getMessage(), e);
        }
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#storeFeed(org.apache.lucene.gdata.data.ServerBaseFeed,
     *      java.lang.String)
     */
    public void storeFeed(ServerBaseFeed feed, String accountname) throws StorageException {
        if (feed == null)
            throw new StorageException("Can not store feed -- is null");
        if (feed.getId() == null)
            throw new StorageException("Can not store feed -- id is null");
        if (feed.getServiceType() == null)
            throw new StorageException("Can not store feed -- service type is null");
        if (accountname == null)
            throw new StorageException("Account name is null");
        Query query = this.container.query();
        query.constrain(ServerBaseFeed.class);
        query.descend("feed").descend("id").constrain(feed.getId()).equal();
        ObjectSet set = query.execute();
        if (set.hasNext())
            throw new StorageException("feed with feedID " + feed.getId() + " is already stored");
        GDataAccount account = getAccount(accountname);
        refreshPersistentObject(account);
        feed.setAccount(account);
        /*
         * service config not required in db4o storage.
         * Entries/Feeds don't have to be build from xml
         */
        feed.setServiceConfig(null);
        try {
            this.container.set(feed);
            this.container.commit();
        } catch (Exception e) {
            LOG.error("Error occured on persisting changes -- rollback changes");
            this.container.rollback();
            throw new StorageException("Can not persist changes -- " + e.getMessage(), e);
        }
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#deleteFeed(java.lang.String)
     */
    public void deleteFeed(String feedId) throws StorageException {
        if (feedId == null)
            throw new StorageException("can not delete feed -- feed id is null");
        Query query = this.container.query();
        query.constrain(ServerBaseFeed.class);
        query.descend("feed").descend("id").constrain(feedId).equal();
        ObjectSet set = query.execute();
        if (set.size() > 1)
            throw new StorageException("Feed query returned not a unique result");
        if (set.size() == 0)
            throw new StorageException("no feed with feedID: " + feedId + " stored -- query returned no result");

        ServerBaseFeed feed = (ServerBaseFeed) set.next();
        refreshPersistentObject(feed);
        ObjectSet entrySet = getEnriesForFeedID(feed.getId());
        try {
            this.container.delete(feed);
            this.container.delete(feed.getFeed());
            for (Object object : entrySet) {
                refreshPersistentObject(object);
                this.container.delete(object);
            }

            this.container.commit();
        } catch (Exception e) {
            LOG.error("Error occured on persisting changes -- rollback changes");
            this.container.rollback();
            throw new StorageException("Can not persist changes -- " + e.getMessage(), e);
        }

    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#updateFeed(org.apache.lucene.gdata.data.ServerBaseFeed,
     *      java.lang.String)
     */
    @SuppressWarnings("unchecked")
    public void updateFeed(ServerBaseFeed feed, String accountname) throws StorageException {
        if (feed == null)
            throw new StorageException("Can not update feed -- is null");
        if (feed.getId() == null)
            throw new StorageException("Can not update feed -- id is null");
        if (feed.getServiceType() == null)
            throw new StorageException("Can not update feed -- service type is null");
        if (accountname == null)
            throw new StorageException("Account name is null");
        GDataAccount account = getAccount(accountname);
        Query query = this.container.query();
        query.constrain(ServerBaseFeed.class);
        query.descend("feed").descend("id").constrain(feed.getId());
        ObjectSet<ServerBaseFeed> set = query.execute();
        if (set.size() > 1)
            throw new StorageException("Query for feed id " + feed.getId() + " returns more than one result");
        if (set.size() < 1)
            throw new StorageException("can not find feed for given feed id -- " + feed.getId());
        ServerBaseFeed result = set.next();
        refreshPersistentObject(result);
        BaseFeed oldFeed = result.getFeed();
        result.setAccount(account);
        result.setFeed(feed.getFeed());
        try {
            this.container.delete(oldFeed);
            this.container.set(result);
            this.container.commit();
        } catch (Exception e) {
            LOG.error("Error occurred on persisting changes -- rollback changes");
            this.container.rollback();
            throw new StorageException("Can not persist changes -- " + e.getMessage(), e);
        }

    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#getServiceForFeed(java.lang.String)
     */
    @SuppressWarnings("unchecked")
    public String getServiceForFeed(final String feedId) throws StorageException {
        if (feedId == null)
            throw new StorageException("can not get Service for feed -- feed id is null");
        if (LOG.isInfoEnabled())
            LOG.info("Retrieving Service for feed -- feed id: " + feedId);
        Query query = this.container.query();
        query.constrain(ServerBaseFeed.class);
        query.descend("feed").descend("id").constrain(feedId);
        ObjectSet<ServerBaseFeed> feed = query.execute();
        if (feed.size() > 1)
            throw new StorageException("Query for feed id " + feedId + " returns more than one result");
        if (feed.size() < 1)
            throw new StorageException("can not find feed for given feed id -- " + feedId);

        ServerBaseFeed result = feed.next();
        if (LOG.isInfoEnabled())
            LOG.info("Retrieved Service for feed -- serviceType: " + result.getServiceType());
        return result.getServiceType();
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#getAccount(java.lang.String)
     */
    public GDataAccount getAccount(String accountName) throws StorageException {
        if (accountName == null)
            throw new StorageException("Can not get account -- account name is null");
        if (LOG.isInfoEnabled())
            LOG.info("Retrieving account for account name: " + accountName);
        Query query = this.container.query();
        query.constrain(GDataAccount.class);
        query.descend("name").constrain(accountName).equal();
        ObjectSet set = query.execute();
        if (set.size() > 1)
            throw new StorageException(
                    "Account query returned not a unique result -- account name: " + accountName);
        if (!set.hasNext())
            throw new ResourceNotFoundException(
                    "No such account stored -- query returned not result for account name: " + accountName);

        return (GDataAccount) set.next();
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#close()
     */
    public void close() {
        releaseAllSemaphore();

    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#getAccountNameForFeedId(java.lang.String)
     */

    public String getAccountNameForFeedId(String feedId) throws StorageException {
        if (feedId == null)
            throw new StorageException("feed id is null");
        GDataAccount account = getServerBaseFeed(feedId).getAccount();
        if (account == null)
            throw new IllegalStateException("No account stored with feed -- feedID: " + feedId);

        return account.getName();
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#getEntryLastModified(java.lang.String, java.lang.String)
     */
    public Long getEntryLastModified(String entryId, final String feedId) throws StorageException {
        if (entryId == null)
            throw new StorageException("Entry ID is null");
        return new Long(getInternalEntry(entryId).getUpdateTime());
    }

    @SuppressWarnings("unchecked")
    private ServerBaseFeed getServerBaseFeed(String feedId) throws StorageException {
        Query query = this.container.query();
        query.constrain(ServerBaseFeed.class);
        query.descend("feed").descend("id").constrain(feedId);
        ObjectSet<ServerBaseFeed> feed = query.execute();
        if (feed.size() > 1)
            throw new StorageException("Query for feed id " + feedId + " returns more than one result");
        if (feed.size() < 1)
            throw new StorageException("can not find feed for given feed id -- " + feedId);
        return feed.next();
    }

    /*
     * !Caution! -- could instantiate a lot of objects if used with certain classes!!
     * Refresh a persisted object with a depth of 100
     * 
     */
    private void refreshPersistentObject(Object o) {
        this.container.ext().refresh(o, 100);
    }

    /**
     * @see org.apache.lucene.gdata.storage.Storage#getFeedLastModified(java.lang.String)
     */
    public Long getFeedLastModified(String feedId) throws StorageException {
        if (feedId == null)
            throw new StorageException("can not get last modified -- id is null");
        ServerBaseFeed feed = getServerBaseFeed(feedId);
        return new Long(feed.getUpdated().getValue());

    }

    private BaseEntry clearDynamicElements(BaseEntry entry) {
        this.container.ext().refresh(entry.getLinks(), 2);
        return entry;
    }

    private BaseFeed clearDynamicElements(BaseFeed feed) {
        this.container.ext().refresh(feed.getLinks(), 2);
        feed.getEntries().clear();
        return feed;
    }

    ObjectContainer getContainer() {
        return this.container;
    }

    static class DB4oEntry {
        private BaseEntry entry;

        private int version;

        private String feedId;

        private long updateTime;

        /**
         * @return Returns the entry.
         */
        protected BaseEntry getEntry() {
            return this.entry;
        }

        /**
         * @param entry
         *            The entry to set.
         */
        protected void setEntry(BaseEntry entry) {
            this.entry = entry;
        }

        /**
         * @return Returns the feed.
         */
        protected String getFeedId() {
            return this.feedId;
        }

        /**
         * @param feed
         *            The feed to set.
         */
        protected void setFeedId(String feed) {
            this.feedId = feed;
        }

        /**
         * @return Returns the updateTime.
         */
        protected long getUpdateTime() {
            return this.updateTime;
        }

        /**
         * @param updateTime
         *            The updateTime to set.
         */
        protected void setUpdateTime(long updateTime) {
            this.updateTime = updateTime;
        }

        /**
         * @return Returns the version.
         */
        public int getVersion() {
            return this.version;
        }

        /**
         * @param version The version to set.
         */
        public void setVersion(int version) {
            this.version = version;
            if (this.entry != null)
                this.entry.setVersionId("" + this.version);
        }

    }

}