org.intermine.api.profile.StorableBag.java Source code

Java tutorial

Introduction

Here is the source code for org.intermine.api.profile.StorableBag.java

Source

package org.intermine.api.profile;

/*
 * Copyright (C) 2002-2013 FlyMine
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  See the LICENSE file for more
 * information or http://www.gnu.org/copyleft/lesser.html.
 *
 */

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.intermine.api.search.OriginatingEvent;
import org.intermine.api.search.DeletionEvent;
import org.intermine.api.search.WebSearchWatcher;
import org.intermine.api.search.WebSearchable;
import org.intermine.api.tag.TagTypes;
import org.intermine.model.userprofile.SavedBag;
import org.intermine.model.userprofile.UserProfile;
import org.intermine.objectstore.ObjectStoreException;
import org.intermine.objectstore.ObjectStoreWriter;
import org.intermine.objectstore.intermine.ObjectStoreInterMineImpl;
import org.intermine.objectstore.intermine.ObjectStoreWriterInterMineImpl;
import org.intermine.objectstore.proxy.ProxyReference;
import org.intermine.objectstore.query.ObjectStoreBag;
import org.intermine.sql.writebatch.Batch;
import org.intermine.sql.writebatch.BatchWriterPostgresCopyImpl;

/**
 * Base class for representations of user's collections of objects stored in a database.
 *
 * @author Alex Kalderimis
 *
 */
public abstract class StorableBag implements WebSearchable {

    private static final Logger LOG = Logger.getLogger(StorableBag.class);

    private enum Order {
        BY_VALUE("value asc"), BY_EXTRA("extra desc");

        private final String fragment;

        private Order(String sqlFragment) {
            this.fragment = sqlFragment;
        }

        @Override
        public String toString() {
            return fragment;
        }
    }

    protected Integer profileId;
    protected Integer savedBagId;

    /**
     * Returns the size of the bag.
     *
     * @return the number of elements in the bag
     * @throws ObjectStoreException if something goes wrong
     */
    public abstract int getSize() throws ObjectStoreException;

    /**
     * Returns the number of elements in the bag.
     * @return the number of elements in the bag
     * @throws ObjectStoreException if something goes wrong
     */
    public int size() throws ObjectStoreException {
        return getSize();
    }

    @Override
    public abstract String getName();

    @Override
    public abstract String getDescription();

    /**
     * Get the date that this bag was created at.
     * @return A date.
     */
    public abstract Date getDateCreated();

    /**
     * Get the unqualified name of class that the objects in this bag represent.
     * @return A name of a class.
     */
    public abstract String getType();

    /** @return the id of the profile belonging to the user this list belongs to */
    public Integer getProfileId() {
        return profileId;
    }

    /** @return the id of the saved bag this list represents */
    public Integer getSavedBagId() {
        return savedBagId;
    }

    /**
     * Return a reference to the object store bag contained in the production database.
     * @return A reference to the backing store of objects.
     */
    protected abstract ObjectStoreBag getOsb();

    /**
     * Return a string representing the state of this bag (eg. CURRENT).
     * @return A valid representation of a BagState
     */
    public abstract String getState();

    /**
     * Get a  reference to a connection to the database where user data is persisted.
     * @return An object capable of storing information in the user data store.
     */
    protected abstract ObjectStoreWriter getUserProfileWriter();

    /**
     * Delete this bag.
     *
     * Contrary to appearances, implementations MUST override this method in order to actually
     * delete the bag. They MUST ALSO call this method (as super.delete()). This is to ensure that
     * bag deletion events are registered correctly.
     *
     * @throws ObjectStoreException 
     */
    public void delete() throws ObjectStoreException {
        fireEvent(new DeletionEvent(this));
    }

    /**
     * Save the bag into the userprofile database, along with information about what kinds of values
     * this bag contains. These bag values can then be used later to reconstruct the contents of the
     * bag if when the bag is used with a different production database.
     *
     * @param profileId the ID of the userprofile
     * @param bagValues the list of the key field values of the objects contained by the bag
     * @throws ObjectStoreException if something goes wrong when inserting data into the database.
     */
    public void saveWithBagValues(Integer profileId, Collection<BagValue> bagValues) throws ObjectStoreException {
        if (profileId == null) {
            throw new NullPointerException("profileId may not be null");
        }
        this.profileId = profileId;
        SavedBag savedBag = storeSavedBag();
        this.savedBagId = savedBag.getId();
        addBagValues(bagValues);
    }

    /**
     * Perform the actual insertion of data into the userprofile database.
     * @return The object that represents the database record for this bag.
     * @throws ObjectStoreException If we cannot store the bag.
     */
    protected SavedBag storeSavedBag() throws ObjectStoreException {
        SavedBag savedBag = new SavedBag();
        savedBag.setId(getSavedBagId());
        if (profileId != null) {
            savedBag.setName(getName());
            savedBag.setType(getType());
            savedBag.setDescription(getDescription());
            savedBag.setDateCreated(getDateCreated());
            savedBag.proxyUserProfile(new ProxyReference(null, profileId, UserProfile.class));
            savedBag.setOsbId(getOsb().getBagId());
            savedBag.setState(getState());
            getUserProfileWriter().store(savedBag);
        }
        return savedBag;
    }

    /** Remove all the values from the bag-value table. **/
    public void deleteAllBagValues() {
        deleteSomeBagValues(null);
    }

    /**
     * Delete a given set of bag values from the bag value table. If an empty list is passed in,
     * no values will be deleted. If null is passed in all values will be deleted.
     * @param values The values to delete. <code>null</code> is understood
     *               as <code>ALL VALUES.</code>.
     */
    protected void deleteSomeBagValues(final List<String> values) {
        Connection conn = null;
        PreparedStatement stm = null;
        ObjectStoreWriter uosw = getUserProfileWriter();
        List<String> clauses = new ArrayList<String>(Arrays.asList("savedBagId = ?"));

        if (values != null) {
            Collection<String> placeHolders = CollectionUtils.collect(values, new ConstantTransformer("?"));
            String valuesList = StringUtils.join(placeHolders, ", ");
            if (!valuesList.isEmpty()) {
                clauses.add("value IN (" + valuesList + ")");
            }
        }

        try {
            conn = ((ObjectStoreWriterInterMineImpl) uosw).getConnection();
            String sql = "DELETE FROM " + InterMineBag.BAG_VALUES + " WHERE " + StringUtils.join(clauses, " AND ");
            stm = conn.prepareStatement(sql);
            stm.setInt(1, savedBagId);
            for (int i = 0; values != null && i < values.size(); i++) {
                stm.setString(i + 2, values.get(i));
            }
            stm.executeUpdate();
        } catch (SQLException sqle) {
            throw new RuntimeException("Error deleting the " + (values == null ? "" : values.size() + " ")
                    + "bagvalues of bag : " + savedBagId, sqle);
        } finally {
            if (stm != null) {
                try {
                    stm.close();
                } catch (SQLException e) {
                    throw new RuntimeException("Problem closing resources", e);
                }
            }
            ((ObjectStoreWriterInterMineImpl) uosw).releaseConnection(conn);
        }
    }

    /**
     * Returns a List of BagValue (key field value and extra value) of the objects contained
     * by this bag.
     * @return the values of the bag.
     */
    public List<BagValue> getContents() {
        return getContents(Order.BY_VALUE);
    }

    /** @return the contents of this list, ordered by their extra-value. **/
    public List<BagValue> getContentsOrderByExtraValue() {
        return getContents(Order.BY_EXTRA);
    }

    /**
     * Get the contents of this list, in a specified order.
     * @param order How to order the items when fetching from the database.
     * @return A list of the contents of the bag.
     */
    private List<BagValue> getContents(Order order) {
        String name = getName();
        ObjectStoreWriter uosw = getUserProfileWriter();
        Connection conn = null;
        Statement stm = null;
        ResultSet rs = null;
        ObjectStoreInterMineImpl uos = null;
        List<BagValue> ret = new ArrayList<BagValue>();

        try {
            uos = (ObjectStoreInterMineImpl) uosw.getObjectStore();
            conn = uos.getConnection();
            stm = conn.createStatement();
            String sql = "SELECT value, extra FROM " + InterMineBag.BAG_VALUES + " WHERE savedbagid = " + savedBagId
                    + " ORDER BY " + order.toString();
            rs = stm.executeQuery(sql);
            while (rs.next()) {
                String value = rs.getString("value");
                String extra = rs.getString("extra") == null ? "" : rs.getString("extra");
                ret.add(new BagValue(value, extra));
            }
        } catch (SQLException sqe) {
            LOG.error("Connection problem while loading primary fields for " + name, sqe);
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                    if (stm != null) {
                        stm.close();
                    }
                } catch (SQLException sqle) {
                    LOG.error("While releasing resources in the method "
                            + "getContentsASPrimaryIdentifierValues for the bag " + name, sqle);
                }
            }
            uos.releaseConnection(conn);
        }
        return ret;
    }

    /**
     * Save the values given in input into bagvalues table
     * @param bagValues the values to save
     */
    protected void addBagValues(Collection<BagValue> bagValues) {
        Connection conn = null;
        Batch batch = null;
        Boolean oldAuto = null;
        ObjectStoreWriter uosw = getUserProfileWriter();
        Integer sbid = getSavedBagId();
        try {
            conn = ((ObjectStoreWriterInterMineImpl) uosw).getConnection();
            oldAuto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            batch = new Batch(new BatchWriterPostgresCopyImpl());
            String[] colNames = new String[] { "savedbagid", "value", "extra" };
            for (BagValue bagValue : bagValues) {
                batch.addRow(conn, InterMineBag.BAG_VALUES, sbid, colNames,
                        new Object[] { sbid, bagValue.value, bagValue.extra });
            }
            batch.flush(conn);
            conn.commit();
            conn.setAutoCommit(oldAuto);
        } catch (SQLException sqle) {
            LOG.error("Exception committing bagValues for bag: " + sbid, sqle);
            try {
                conn.rollback();
                if (oldAuto != null) {
                    conn.setAutoCommit(oldAuto);
                }
            } catch (SQLException sqlex) {
                throw new RuntimeException("Error aborting transaction", sqlex);
            }
        } finally {
            try {
                batch.close(conn);
            } catch (Exception e) {
                LOG.error("Exception caught when closing Batch while addbagValues", e);
            }
            ((ObjectStoreWriterInterMineImpl) uosw).releaseConnection(conn);
        }
    }

    // WebSearchable Implementation //
    private final Set<WebSearchWatcher> observers = new HashSet<WebSearchWatcher>();

    @Override
    public void addObserver(WebSearchWatcher wsw) {
        observers.add(wsw);
    }

    @Override
    public void removeObserver(WebSearchWatcher wsw) {
        observers.remove(wsw);
    }

    @Override
    public String getTagType() {
        return TagTypes.BAG;
    }

    @Override
    public void fireEvent(OriginatingEvent e) {
        if (observers != null) { // Can be due the order of initialisation of static fields...
            Collection<WebSearchWatcher> watchers = new ArrayList<WebSearchWatcher>(observers);
            for (WebSearchWatcher wsw : watchers) {
                wsw.receiveEvent(e);
            }
        }
    }

}