de.nava.informa.impl.hibernate.ChannelBuilder.java Source code

Java tutorial

Introduction

Here is the source code for de.nava.informa.impl.hibernate.ChannelBuilder.java

Source

//
// Informa -- RSS Library for Java
// Copyright (c) 2002 by Niko Schmuck
//
// Niko Schmuck
// http://sourceforge.net/projects/informa
// mailto:niko_schmuck@users.sourceforge.net
//
// This library is free software.
//
// You may redistribute it and/or modify it under the terms of the GNU
// Lesser General Public License as published by the Free Software Foundation.
//
// Version 2.1 of the license should be included with this distribution in
// the file LICENSE. If the license is not included with this distribution,
// you may find a copy at the FSF web site at 'www.gnu.org' or 'www.fsf.org',
// or you may write to the Free Software Foundation, 675 Mass Ave, Cambridge,
// MA 02139 USA.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied waranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//

// $Id: ChannelBuilder.java,v 1.29 2006/12/04 23:43:27 italobb Exp $

package de.nava.informa.impl.hibernate;

import java.net.URL;
import java.util.Date;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.jdom.Element;

import de.nava.informa.core.CategoryIF;
import de.nava.informa.core.ChannelBuilderException;
import de.nava.informa.core.ChannelBuilderIF;
import de.nava.informa.core.ChannelGroupIF;
import de.nava.informa.core.ChannelIF;
import de.nava.informa.core.CloudIF;
import de.nava.informa.core.ImageIF;
import de.nava.informa.core.ItemEnclosureIF;
import de.nava.informa.core.ItemGuidIF;
import de.nava.informa.core.ItemIF;
import de.nava.informa.core.ItemSourceIF;
import de.nava.informa.core.TextInputIF;

/**
 * Factory for the creation of the channel object model with the hibernate
 * persistent store.
 * <p/>
 * NOT THREAD SAFE
 * <p/>
 * Hibernate Multi-threading notes:
 * ChannelBuilder has some subtleties as it relates to threading. The specifics
 * of the way it is supported still need to be proven. Certainly the error
 * handling here and in UpdateChannelTask and in ChannelRegistry is incomplete.
 * It seems to work, but I would consider it incomplete still.
 * <p/>
 * The key facts are
 * (1) Sessions are not thread safe and
 * (2) Sessions should have relatively short lifespans.
 * <p/>
 * To support this, there is a mode of using
 * ChannelBuilder where it holds on to a SessionHandler and manages the creation
 * and destruction of Sessions on behalf of the caller. When you supply a
 * SessionHandler to ChannelBuilder, you may use the beginTransaction() and
 * endTransaction() calls to take all the steps needed before and after a
 * transaction. At the end of endTransaction() the transaction will be closed
 * and the session will be flushed and closed. To use this mode, you should
 * (1) Create a SessionHandler ,
 * (2) Create a JDBC Connection to the database,
 * (3) sessionHandler.setConnection(connection), and
 * (4) use new ChannelBuilder(sessionHandler).
 *
 * @author Niko Schmuck (niko@nava.de)
 */
public class ChannelBuilder implements ChannelBuilderIF {

    private static final Log LOG = LogFactory.getLog(ChannelBuilder.class);

    private Session session;

    private SessionHandler handler;

    private Transaction transaction;

    /**
     * ChannelBuilder constructor. Caller is responsible for managing sessions and
     * transactions.
     */
    public ChannelBuilder(Session session) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("New Channel Builder for: " + session);
        }
        this.session = session;
        this.handler = null;
    }

    /**
     * ChannelBuilder constructor. ChannelBuilder will manage sessions and
     * transactions. Supplied SessionHandler needs to have a live JDBC connection
     * available.
     */
    public ChannelBuilder(SessionHandler handler) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("New Channel Builder for: " + handler);
        }
        this.handler = handler;
        this.session = null;
    }

    // --------------------------------------------------------------
    // Hibernate Specific Methods
    // --------------------------------------------------------------

    /**
     * Processing needed at the start of a transaction. - creating a session -
     * beginning the transaction
     */
    public void beginTransaction() throws ChannelBuilderException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("beginTransaction");
        }
        if (session != null || handler == null)
            throw new IllegalStateException("Session != null || handler == null");
        try {
            session = handler.getSession();
            transaction = session.beginTransaction();
        } catch (HibernateException e) {
            transaction = null;
            throw new ChannelBuilderException(e);
        }
    }

    /**
     * Processing needed at the end of a transaction. - commit the transaction -
     * flush the session - close the session TODO: catch the exception so this
     * method doesn't have any throws.
     */
    public void endTransaction() throws ChannelBuilderException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("endTransaction");
        }
        if (handler == null || transaction == null || session == null)
            throw new IllegalStateException("handler == null || transaction == null || session == null");
        try {
            transaction.commit();
            session.flush();
            session.close();
            session = null;
            transaction = null;
        } catch (HibernateException he) {
            if (transaction != null) {
                try {
                    transaction.rollback();
                    transaction = null;
                    if (session.isOpen()) {
                        session.close();
                        session = null;
                    }
                } catch (HibernateException e) {
                    if (session.isOpen()) {
                        session = null;
                    }
                    throw new ChannelBuilderException(e);
                }
            }
            throw new ChannelBuilderException(he);
        }
    }

    /**
     * Check if we are already in the middle of a transaction. This is needed
     * because as of now begin/endTransactions cannot be nested and in fact give
     * assert errors if you try.
     *
     * @return - boolean indicating whether we are currently in a transaction.
     */
    public boolean inTransaction() {
        return session != null && transaction != null;
    }

    /**
     * resetTransaction - Used during error handling. If in a catch block there is
     * a potentially still open transaction (i.e. beginTransaction() was called)
     * then call this method to reset the state of the ChannelBuilder and clean up
     * the transaction.
     */
    public void resetTransaction() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Transaction being reset.");
        }
        if (transaction != null) {
            try {
                transaction.commit();
                transaction = null;
            } catch (HibernateException e) {
                transaction = null;
            }
        }
        if (session != null) {
            try {
                session.flush();
                session.close();
                session = null;
            } catch (HibernateException e) {
                session = null;
            }
        }
    }

    /**
     * Certain Hibernate calls require the session. Note that this call should
     * only be made between a beginTransaction and endTransaction call which is
     * why we throw an IllegalStateException otherwise.
     */
    public Session getSession() {
        if (handler == null || session == null)
            throw new IllegalStateException("getSession must be bracketed by begin/endTransaction");
        if (!handler.isSessionOpen())
            throw new IllegalStateException("Hibernate Handler must be open");
        return session;
    }

    /**
     * update - Hibernate Update some object
     *
     * @param o
     * @throws ChannelBuilderException -
     */
    public void update(Object o) throws ChannelBuilderException {
        try {
            session.update(o);
        } catch (HibernateException e) {
            throw new ChannelBuilderException("update() Failed");
        }
    }

    /**
     * Hibernate Delete some object
     *
     * @param o -
     *          Object to Delete
     * @throws ChannelBuilderException -
     *                                 Translation of Hibernate exception
     */
    public void delete(Object o) throws ChannelBuilderException {
        try {
            session.delete(o);
        } catch (HibernateException e) {
            throw new ChannelBuilderException("delete() Failed");
        }
    }

    // --------------------------------------------------------------
    // implementation of ChannelBuilderIF interface
    // --------------------------------------------------------------

    public void init(Properties props) throws ChannelBuilderException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("initialising channel builder for hibernate backend");
        }
    }

    public ChannelGroupIF createChannelGroup(String title) {
        ChannelGroupIF obj = new ChannelGroup(title);
        save(obj);
        return obj;
    }

    public ChannelIF createChannel(String title) {
        return createChannel((Element) null, title);
    }

    public ChannelIF createChannel(Element channelElement, String title) {
        return createChannel(channelElement, title, null);
    }

    public ChannelIF createChannel(String title, String location) {
        return createChannel(null, title, location);
    }

    /**
     * May throw runtime HibernateException
     */
    public ChannelIF createChannel(Element channelElement, String title, String location) {
        ChannelIF obj = null;
        if (location != null) {
            Query query = session.createQuery("from Channel as channel where channel.locationString = ? ");
            query.setString(0, location);
            obj = (ChannelIF) query.uniqueResult();
        }
        if (obj == null) {
            obj = new Channel(channelElement, title, location);
            session.save(obj);
        } else {
            LOG.info("Found already existing channel instance with location " + location);
        }
        return obj;
    }

    public ItemIF createItem(ChannelIF channel, String title, String description, URL link) {
        return createItem(null, channel, title, description, link);
    }

    public ItemIF createItem(Element itemElement, ChannelIF channel, String title, String description, URL link) {
        // according to RSS 2.0 spec link may be omitted, but need link
        // for unique identifier.  Add channel location for uniqueness?
        //
        if (link == null) {
            throw new RuntimeException("link required for item " + title + " for persistence uniqueness");
        }

        Query query = session.createQuery("from Item as item where item.linkString = ? ");
        query.setString(0, link.toString());
        ItemIF obj = (ItemIF) query.uniqueResult();
        if (obj == null) {
            obj = new Item(channel, title, description, link);
            if (channel != null) {
                channel.addItem(obj);
            }
            session.save(obj);
        } else if (LOG.isDebugEnabled()) {
            LOG.debug("Found already existing item instance with location " + link);
        }
        return obj;
    }

    public ItemIF createItem(ChannelIF channel, ItemIF item) {
        throw new RuntimeException("Not implemented yet.");
    }

    public ImageIF createImage(String title, URL location, URL link) {
        Query query = session.createQuery("from Image as img where img.locationString = ? ");
        query.setString(0, location.toString());
        ImageIF obj = (Image) query.uniqueResult();
        if (obj == null) {
            obj = new Image(title, location, link);
            session.save(obj);
        }
        return obj;
    }

    public CloudIF createCloud(String domain, int port, String path, String registerProcedure, String protocol) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("ChannelBuilder is creating a Persistent Cloud");
        }
        // equality by domain, port, and path

        Query query = session
                .createQuery("from Cloud as cld where cld.domain = ? and cld.port = ? and cld.path = ?");
        query.setString(0, domain);
        query.setInteger(1, port);
        query.setString(2, path);
        CloudIF obj = (CloudIF) query.uniqueResult();
        if (obj == null) {
            obj = new Cloud(domain, port, path, registerProcedure, protocol);
            session.save(obj);
        }

        return obj;
    }

    public TextInputIF createTextInput(String title, String description, String name, URL link) {
        Query query = session
                .createQuery("from TextInput as txt where txt.title = ? and txt.name = ? and txt.linkString = ? ");
        query.setString(0, title);
        query.setString(1, name);
        query.setString(2, link.toString());
        TextInputIF obj = (TextInput) query.uniqueResult();
        if (obj == null) {
            obj = new TextInput(title, description, name, link);
            session.save(obj);
        }
        return obj;
    }

    public ItemSourceIF createItemSource(ItemIF item, String name, String location, Date timestamp) {
        // TODO Auto-generated method stub
        return null;
    }

    public ItemSourceIF createItemSource(String name, String location, Date timestamp) {

        Query query = session.createQuery(
                "from ItemSource as src where src.name = ? and src.location = ? and src.timestamp = ?  ");
        query.setString(0, name);
        query.setString(1, location);
        query.setTimestamp(2, timestamp);
        ItemSourceIF obj = (ItemSourceIF) query.uniqueResult();
        if (obj == null) {
            obj = new ItemSource(null, name, location, timestamp);
            session.save(obj);
        }
        return obj;
    }

    public ItemEnclosureIF createItemEnclosure(ItemIF item, URL location, String type, int length) {
        Query query = session.createQuery("from ItemEnclosure as enc where enc.item.id = ? ");
        query.setLong(0, item.getId());
        ItemEnclosureIF obj = (ItemEnclosureIF) query.uniqueResult();
        if (obj == null) {
            obj = new ItemEnclosure(item, location, type, length);
            session.save(obj);
        }
        return obj;
    }

    public ItemGuidIF createItemGuid(ItemIF item, String location, boolean permaLink) {
        Query query = session.createQuery("from ItemGuid as guid where guid.location = ? ");
        query.setString(0, location);
        ItemGuidIF guid = (ItemGuidIF) query.uniqueResult();
        if (guid == null) {
            guid = new ItemGuid(item, location, permaLink);
            guid.setPermaLink(permaLink);
            session.save(guid);
        }
        return guid;
    }

    public CategoryIF createCategory(CategoryIF parent, String title) {
        return createCategory(parent, title, null);
    }

    public CategoryIF createCategory(CategoryIF parent, String title, String domain) {
        Query query = session.createQuery("from Category as cat where cat.title = ? and cat.domain = ? ");
        query.setString(0, title);
        query.setString(1, domain);
        CategoryIF cat = (CategoryIF) query.uniqueResult();
        if (cat == null) {
            cat = new Category(title);
            cat.setDomain(domain);
            if (parent != null) {
                parent.addChild(cat);
            }
            session.save(cat);
        }
        return cat;
    }

    public void close() throws ChannelBuilderException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("closing channel builder for hibernate backend");
        }
    }

    /**
     * Reloads group for use in new session.
     *
     * @param group to reload.
     * @return reloaded group for chaning.
     * @throws ChannelBuilderException when unable to reload data.
     */
    public ChannelGroup reload(ChannelGroup group) throws ChannelBuilderException {
        try {
            getSession().load(group, group.getId());
        } catch (HibernateException e) {
            throw new ChannelBuilderException("Unable to reload group: " + e.getMessage());
        }
        return group;
    }

    /**
     * Reloads channel for use in new session.
     *
     * @param channel channel to reload.
     * @return reloaded channel for chaining.
     * @throws ChannelBuilderException when unable to reload data.
     */
    public Channel reload(Channel channel) throws ChannelBuilderException {
        try {
            getSession().load(channel, channel.getId());
        } catch (HibernateException e) {
            throw new ChannelBuilderException("Unable to reload channel: " + e.getMessage());
        }
        return channel;
    }

    /**
     * Reloads item for use in new session.
     *
     * @param item item to reload.
     * @return reloaded item for chaning.
     * @throws ChannelBuilderException when unable to reload data.
     */
    public Item reload(Item item) throws ChannelBuilderException {
        try {
            getSession().load(item, item.getId());
        } catch (HibernateException e) {
            throw new ChannelBuilderException("Unable to reload item: " + e.getMessage());
        }
        return item;
    }

    // -------------------------------------------------------------
    // internal helper methods
    // -------------------------------------------------------------

    protected void save(Object dataObject) {
        if (session == null)
            throw new IllegalStateException("Session == null");
        try {
            session.save(dataObject);
        } catch (HibernateException he) {
            throw new RuntimeException(he.getMessage());
        }
    }
}