fr.keyconsulting.oliphant.NotifyListener.java Source code

Java tutorial

Introduction

Here is the source code for fr.keyconsulting.oliphant.NotifyListener.java

Source

/*******************************************************************************
    
   Copyright (C) 2009-2010 Key Consulting
    
   This file is part of Oliphant.
     
   Oliphant is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
     
   Oliphant is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.
     
   You should have received a copy of the GNU Lesser General Public
   License along with Oliphant.  If not, see <http://www.gnu.org/licenses/>.
    
*******************************************************************************/

package fr.keyconsulting.oliphant;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.StaleObjectStateException;
import org.hibernate.cache.CacheKey;
import org.hibernate.cache.access.EntityRegionAccessStrategy;
import org.hibernate.cache.entry.CacheEntry;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.event.EventSource;
import org.hibernate.event.FlushEntityEvent;
import org.hibernate.event.FlushEntityEventListener;
import org.hibernate.event.PersistEvent;
import org.hibernate.event.PersistEventListener;
import org.hibernate.event.PostLoadEvent;
import org.hibernate.event.PostLoadEventListener;
import org.hibernate.event.PreUpdateEvent;
import org.hibernate.event.PreUpdateEventListener;
import org.hibernate.persister.entity.EntityPersister;
import org.postgresql.util.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NotifyListener
        implements PostLoadEventListener, PersistEventListener, FlushEntityEventListener, PreUpdateEventListener {
    private static final long serialVersionUID = -8582214998956097719L;
    private Map<String, String> versions = new HashMap<String, String>(); // Maps object UIDs to latest known versions
    private SessionFactoryImplementor sessionFactory;
    private SpecificNotifyListener specificNotifyListener;
    private boolean allowStaleLoad = true;
    private Configuration config;

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

    public void onPostLoad(PostLoadEvent event) throws StaleObjectStateException {
        LOG.debug("Hibernate: Post load event");
        processLoadEvent(event, true);
        if (!allowStaleLoad) {
            updateStaleUidsAndVersions();
            checkObject(event.getEntity(), event.getSession());
        }
    }

    public void onPersist(PersistEvent event, Map map) throws StaleObjectStateException {
        LOG.debug("Hibernate:  Persist event");
        updateStaleUidsAndVersions();
        checkObject(event.getObject(), event.getSession());
    }

    public void onPersist(PersistEvent event) throws StaleObjectStateException {
        LOG.debug("Hibernate:  Persist event");
        updateStaleUidsAndVersions();
        checkObject(event.getObject(), event.getSession());
    }

    public void onFlushEntity(FlushEntityEvent event) throws StaleObjectStateException {
        LOG.debug("Hibernate:  Flush entity event");
        updateStaleUidsAndVersions();
        checkObject(event.getEntity(), event.getSession());
    }

    public boolean onPreUpdate(PreUpdateEvent event) {
        LOG.debug("Hibernate:  Pre-update event");
        updateStaleUidsAndVersions();
        checkObject(event.getEntity(), event.getSession());
        return true;
    }

    public Serializable processLoadEvent(PostLoadEvent event, boolean throwStaleException)
            throws StaleObjectStateException {
        if (sessionFactory == null) {
            // our first event, initialize the listener
            sessionFactory = (SessionFactoryImplementor) event.getSession().getSessionFactory();
            specificNotifyListener.setUp();
        }
        Object object = event.getEntity();
        EventSource session = event.getSession();
        EntityPersister persister = event.getPersister();
        String uid = getUid(object, session);
        if (persister.isVersioned()) {
            if (!versions.containsKey(uid)) {
                // We have not yet received notifications for this object
                versions.put(uid, Base64
                        .encodeBytes(persister.getVersion(object, session.getEntityMode()).toString().getBytes()));
            }
        }
        return true;
    }

    public Serializable checkObject(Object object, EventSource session) throws StaleObjectStateException {
        Serializable identifier = session.getIdentifier(object);
        LOG.debug("* Checking object " + identifier + " : ");
        if (isKnownToBeStaleInSession(object, session)) {
            LOG.debug("Object is stale in session");
            String entityName = session.getEntityName(object);
            if (isKnownToBeStaleInL2(object, session)) {
                LOG.debug(" and in L2 cache");
                evictFromL2(object, session);
            }
            throw new StaleObjectStateException(entityName, identifier);
        }
        LOG.debug("Object is not verifiably stale");
        return null;
    }

    public boolean isKnownToBeStaleInL2(Object object, EventSource session) {
        final EntityPersister persister = sessionFactory.getEntityPersister(session.getEntityName(object));
        String uid = getUid(object, session);
        if (!versions.containsKey(uid)) {
            return false;
        }
        if (persister.isVersioned()) {
            if (persister.hasCache() && session.getCacheMode().isGetEnabled()) {
                final EntityRegionAccessStrategy cacheAccessStrategy = persister.getCacheAccessStrategy();
                if (cacheAccessStrategy == null) {
                    return false;
                }
                final CacheKey ck = new CacheKey(session.getIdentifier(object), persister.getIdentifierType(),
                        persister.getRootEntityName(), session.getEntityMode(), session.getFactory());
                CacheEntry cachedObject = (CacheEntry) cacheAccessStrategy.get(ck, Long.MAX_VALUE);
                if (cachedObject == null) {
                    return false;
                }
                if (Base64.encodeBytes(cachedObject.getDisassembledState()[persister.getVersionProperty()]
                        .toString().getBytes()) != versions.get(uid)) {
                    return true;
                }
            }
        }
        return false;
    }

    public void evictFromL2(Object object, EventSource session) {
        final EntityPersister persister = sessionFactory.getEntityPersister(session.getEntityName(object));
        if (persister.isVersioned()) {
            if (persister.hasCache() && session.getCacheMode().isGetEnabled()) {
                final EntityRegionAccessStrategy cacheAccessStrategy = persister.getCacheAccessStrategy();
                if (cacheAccessStrategy == null) {
                    return;
                }
                final CacheKey ck = new CacheKey(session.getIdentifier(object), persister.getIdentifierType(),
                        persister.getRootEntityName(), session.getEntityMode(), session.getFactory());
                cacheAccessStrategy.evict(ck);
                Serializable identifier = session.getIdentifier(object);
                LOG.debug("* Object " + identifier + " evicted from L2");
            }
        }
    }

    public boolean isKnownToBeStaleInSession(Object object, EventSource session) {
        String uid = getUid(object, session);
        updateStaleUidsAndVersions();
        if (versions.containsKey(uid)) {
            EntityPersister persister = session.getEntityPersister(session.getEntityName(object), object);
            String version = Base64
                    .encodeBytes(persister.getVersion(object, session.getEntityMode()).toString().getBytes());
            if (!version.equals(versions.get(uid))) {
                return true;
            }
        }
        return false;
    }

    private String getUid(Object object, EventSource session) {
        String entityName = session.getEntityName(object);
        String tableName = config.getClassMapping(entityName).getTable().getName().toLowerCase();
        String id = Base64.encodeBytes(session.getIdentifier(object).toString().getBytes());

        return tableName + "#" + id;
    }

    private void updateStaleUidsAndVersions() {
        List<Notification> updates = specificNotifyListener.getLatestUpdates();
        for (int i = 0; i < updates.size(); i++) {
            Notification notif = updates.get(i);
            if (notif.getVersion() != null) {
                versions.put(notif.getUid(), notif.getVersion());
            }
        }
    }

    public static void attachListener(Configuration config) {
        NotifyListener listener = new NotifyListener();

        listener.config = config;

        PostLoadEventListener[] originalPostLoadListeners = config.getEventListeners().getPostLoadEventListeners();
        int originalPostLoadListenersSize = java.lang.reflect.Array.getLength(originalPostLoadListeners);
        PostLoadEventListener[] postLoadEventListeners = new PostLoadEventListener[originalPostLoadListenersSize
                + 1];
        postLoadEventListeners[0] = listener;
        System.arraycopy(originalPostLoadListeners, 0, postLoadEventListeners, 1, originalPostLoadListenersSize);
        config.getEventListeners().setPostLoadEventListeners(postLoadEventListeners);

        PersistEventListener[] originalPersistEventListeners = config.getEventListeners()
                .getPersistEventListeners();
        int originalPersistEventListenersSize = java.lang.reflect.Array.getLength(originalPersistEventListeners);
        PersistEventListener[] persistEventListeners = new PersistEventListener[originalPersistEventListenersSize
                + 1];
        persistEventListeners[0] = listener;
        System.arraycopy(originalPersistEventListeners, 0, persistEventListeners, 1,
                originalPersistEventListenersSize);
        config.getEventListeners().setPersistEventListeners(persistEventListeners);

        FlushEntityEventListener[] originalFlushEntityEventListeners = config.getEventListeners()
                .getFlushEntityEventListeners();
        int originalFlushEntityEventListenersSize = java.lang.reflect.Array
                .getLength(originalFlushEntityEventListeners);
        FlushEntityEventListener[] flushEntityEventListeners = new FlushEntityEventListener[originalFlushEntityEventListenersSize
                + 1];
        flushEntityEventListeners[0] = listener;
        System.arraycopy(originalFlushEntityEventListeners, 0, flushEntityEventListeners, 1,
                originalFlushEntityEventListenersSize);
        config.getEventListeners().setFlushEntityEventListeners(flushEntityEventListeners);

        PreUpdateEventListener[] originalPreUpdateEventListeners = config.getEventListeners()
                .getPreUpdateEventListeners();
        int originalPreUpdateEventListenersSize = java.lang.reflect.Array
                .getLength(originalPreUpdateEventListeners);
        PreUpdateEventListener[] preUpdateEventListeners = new PreUpdateEventListener[originalPreUpdateEventListenersSize
                + 1];
        preUpdateEventListeners[0] = listener;
        System.arraycopy(originalPreUpdateEventListeners, 0, preUpdateEventListeners, 1,
                originalPreUpdateEventListenersSize);
        config.getEventListeners().setPreUpdateEventListeners(preUpdateEventListeners);
        try {
            Class specListClass = Class.forName(config.getProperty("oliphant.specific_listener"));
            listener.specificNotifyListener = (SpecificNotifyListener) specListClass.newInstance();
            listener.specificNotifyListener.prepare(config);
        } catch (ClassNotFoundException e) {
            throw new HibernateException(e);
        } catch (InstantiationException e) {
            throw new HibernateException(e);
        } catch (IllegalAccessException e) {
            throw new HibernateException(e);
        }
        String allowStaleString = config.getProperty("oliphant.allow_stale_load");
        if ((allowStaleString != null) && (allowStaleString.equals("false"))) {
            listener.allowStaleLoad = false;
        }
    }
}