org.headsupdev.agile.storage.HibernateStorage.java Source code

Java tutorial

Introduction

Here is the source code for org.headsupdev.agile.storage.HibernateStorage.java

Source

/*
 * HeadsUp Agile
 * Copyright 2009-2013 Heads Up Development Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.headsupdev.agile.storage;

import org.headsupdev.agile.api.logging.Logger;
import org.headsupdev.agile.storage.resource.ResourceManagerImpl;
import org.headsupdev.support.java.StringUtil;
import org.hibernate.Criteria;
import org.hibernate.Transaction;
import org.hibernate.Session;
import org.hibernate.Query;

import java.util.*;
import java.io.*;
import java.nio.channels.FileChannel;

import org.headsupdev.agile.api.*;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

/**
 * Backing the data to a real database using hibernate.
 *
 * @author Andrew Williams
 * @version $Id$
 * @since 1.0
 */
public class HibernateStorage implements Storage, Serializable {
    private static ThreadLocal<org.hibernate.classic.Session> threadSession = new ThreadLocal<org.hibernate.classic.Session>();
    private static ThreadLocal<Object> currentScope = new ThreadLocal<Object>();
    private static Map<Object, org.hibernate.classic.Session> sessions = new HashMap<Object, org.hibernate.classic.Session>();

    private static HeadsUpConfiguration globalConfig;
    private static ResourceManagerImpl resourceManager;

    private static final long RECENT_PROJECTS_OFFSET = 1000l * 60 * 60 * 24 * 7;
    private static final long ACTIVE_PROJECTS_OFFSET = 1000l * 60 * 60 * 24 * 28;
    private static Set<String> activeProjectIds;
    private static Map<String, Date> latestProjectActivity;
    private static Map<String, Set<String>> recentProjectIds;
    private static Map<String, Map<String, Date>> latestUserProjectActivity;

    private static boolean loaded = false;

    protected void load() {
        if (loaded) {
            return;
        }
        loaded = true;

        activeProjectIds = new HashSet<String>();
        latestProjectActivity = new HashMap<String, Date>();
        List<Event> activeEvents = getEvents(new Date(System.currentTimeMillis() - ACTIVE_PROJECTS_OFFSET),
                new Date());
        for (Event event : activeEvents) {
            addActiveProjectForEvent(event);
        }

        recentProjectIds = new HashMap<String, Set<String>>();
        latestUserProjectActivity = new HashMap<String, Map<String, Date>>();
        List<Event> recentEvents = getEvents(new Date(System.currentTimeMillis() - RECENT_PROJECTS_OFFSET));
        for (Event event : recentEvents) {
            addRecentProjectForEvent(event);
        }

        scheduleRemoveInactiveProjects();
    }

    public HibernateStorage() {
        load();
    }

    protected static void removeInactiveProjects() {
        Date oldestActiveDate = new Date(System.currentTimeMillis() - ACTIVE_PROJECTS_OFFSET);
        Iterator<String> keyIterator = latestProjectActivity.keySet().iterator();
        while (keyIterator.hasNext()) {
            String projectId = keyIterator.next();

            Date latestActivity = latestProjectActivity.get(projectId);
            if (latestActivity.before(oldestActiveDate)) {
                keyIterator.remove();
            }
        }

        Date oldestRecentDate = new Date(System.currentTimeMillis() - RECENT_PROJECTS_OFFSET);
        for (String username : latestUserProjectActivity.keySet()) {
            Map<String, Date> latestUserActivity = latestUserProjectActivity.get(username);

            keyIterator = latestUserActivity.keySet().iterator();
            while (keyIterator.hasNext()) {
                String projectId = keyIterator.next();

                Date latestRecent = latestUserActivity.get(projectId);
                if (latestRecent.before(oldestRecentDate)) {
                    keyIterator.remove();
                }
            }
        }
    }

    protected static void scheduleRemoveInactiveProjects() {
        new Thread() {
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000l * 60 * 60 * 6);
                    } catch (InterruptedException e) {
                        // ignore
                    }

                    Logger log = Manager.getLogger("HibernateStorage-InactiveProjects");
                    log.debug("Removing inactive or non-recent projects");
                    removeInactiveProjects();
                }
            }
        }.start();
    }

    protected static void addActiveProjectForEvent(Event event) {
        if (event.getProject() == null || event.getProject().getId().equals(Project.ALL_PROJECT_ID)) {
            return;
        }

        String projectId = event.getProject().getId();

        activeProjectIds.add(projectId);
        latestProjectActivity.put(projectId, event.getTime());
    }

    protected static void addRecentProjectForEvent(Event event) {
        if (event.getProject() == null || event.getProject().getId().equals(Project.ALL_PROJECT_ID)) {
            return;
        }
        String projectId = event.getProject().getId();

        if (event.getUsername() == null) {
            return;
        }

        Set<String> userRecentProjectIds = recentProjectIds.get(event.getUsername());
        if (userRecentProjectIds == null) {
            userRecentProjectIds = new HashSet<String>();
            recentProjectIds.put(event.getUsername(), userRecentProjectIds);
        }
        userRecentProjectIds.add(projectId);

        Map<String, Date> projectActivity = latestUserProjectActivity.get(event.getUsername());
        if (projectActivity == null) {
            projectActivity = new HashMap<String, Date>();
            latestUserProjectActivity.put(event.getUsername(), projectActivity);
        }
        projectActivity.put(projectId, event.getTime());
    }

    public Session getHibernateSession() {
        return getCurrentSession();
    }

    static Session getCurrentSession() {
        return HibernateUtil.getCurrentSession();
    }

    public void enterScope(Object scope) {
        currentScope.set(scope);
    }

    public void exitScope(Object scope) {
        synchronized (sessions) {
            if (sessions.containsKey(currentScope.get())) {
                Session session = sessions.remove(currentScope.get());
                session.close();
            }
        }

        currentScope.set(null);
    }

    public static Object getCurrentScope() {
        return currentScope.get();
    }

    public static Map<Object, org.hibernate.classic.Session> getManagedSessions() {
        return sessions;
    }

    // TODO try and move this out - impact?
    public void closeSession() {
        Session s = threadSession.get();
        if (s != null) {
            s.close();
            threadSession.set(null);
        }
    }

    public File getDataDirectory() {
        return getGlobalConfiguration().getDataDir();
    }

    public File getWorkingDirectory(Project project) {
        File checkoutDir = new File(getDataDirectory(), "checkout");
        if (!checkoutDir.exists()) {
            checkoutDir.mkdir();
        }

        String rootId = project.getId();
        String rootScm = project.getScm();
        Project parent = project;
        while (parent.getParent() != null) {
            parent = parent.getParent();

            rootId = parent.getId();
            rootScm = parent.getScm();
        }

        String folders = "";
        if (!project.getScm().equals(rootScm)) {
            folders = File.separatorChar + project.getScm().substring(rootScm.length());
        }

        return new File(checkoutDir, rootId + folders);
    }

    public void copyWorkingDirectory(Project project, File dest) throws IOException {
        File src = getWorkingDirectory(project);

        copyFiles(src, dest);
    }

    private void copyFiles(File src, File dest) throws IOException {
        if (src.isDirectory()) {
            dest.mkdirs();
            String[] files = src.list();

            for (String fileName : files) {
                File src1 = new File(src, fileName);
                File dest1 = new File(dest, fileName);

                copyFiles(src1, dest1);
            }
        } else {
            FileChannel sourceChannel = new FileInputStream(src).getChannel();
            FileChannel targetChannel = new FileOutputStream(dest).getChannel();
            sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);
            sourceChannel.close();
            targetChannel.close();
        }
    }

    public File getApplicationDataDirectory(Application app) {
        if (app == null) {
            return getDataDirectory();
        }

        File ret = new File(getDataDirectory(), app.getApplicationId());

        if (!ret.exists()) {
            ret.mkdir();
        }

        return ret;
    }

    public String getConfigurationItem(String name) {
        Session session = getHibernateSession();
        Transaction tx = session.beginTransaction();
        StoredConfigurationItem item = (StoredConfigurationItem) session
                .createQuery("from StoredConfigurationItem i where name = '" + name + "'").uniqueResult();
        tx.commit();

        if (item == null) {
            return null;
        }
        return item.getValue();
    }

    public void setConfigurationItem(String name, String value) {
        Session session = getHibernateSession();
        Transaction tx = session.beginTransaction();
        StoredConfigurationItem item = (StoredConfigurationItem) session
                .createQuery("from StoredConfigurationItem i where name = '" + name + "'").uniqueResult();

        boolean requiresReload = false;
        if (item == null) {
            item = new StoredConfigurationItem(name, value);
            requiresReload = true;
        } else {
            item.setValue(value);
        }

        session.saveOrUpdate(item);
        tx.commit();

        //        if ( requiresReload )
        //        {
        //            reloadGlobalConfiguration();
        //        }
    }

    public void removeConfigurationItem(String name) {
        Session session = getHibernateSession();
        Transaction tx = session.beginTransaction();

        session.createQuery("delete from StoredConfigurationItem i where name = '" + name + "'").executeUpdate();

        tx.commit();
    }

    public Map<String, String> getConfigurationItems(String prefix) {
        Map<String, String> ret = new HashMap<String, String>();
        Session session = getHibernateSession();
        Transaction tx = session.beginTransaction();
        List<StoredConfigurationItem> items = (List<StoredConfigurationItem>) session
                .createQuery("from StoredConfigurationItem i where name like '" + prefix + "%'").list();
        tx.commit();

        for (StoredConfigurationItem item : items) {
            ret.put(item.getName(), item.getValue());
        }

        return ret;
    }

    public void setConfigurationItems(Map<String, String> items) {
        Session session = getHibernateSession();
        Transaction tx = session.beginTransaction();

        for (String name : items.keySet()) {
            StoredConfigurationItem item = (StoredConfigurationItem) session
                    .createQuery("from StoredConfigurationItem i where name = '" + name + "'").uniqueResult();

            if (item == null) {
                item = new StoredConfigurationItem(name, items.get(name));
            } else {
                item.setValue(items.get(name));
            }

            session.saveOrUpdate(item);
        }
        tx.commit();
    }

    public HeadsUpConfiguration getGlobalConfiguration() {
        if (globalConfig == null) {
            // TODO does this need to invalidate session?
            globalConfig = new HeadsUpConfiguration(getConfigurationItems(""));
        }

        return globalConfig;
    }

    private void reloadGlobalConfiguration() {
        globalConfig = null;
    }

    public ResourceManagerImpl getResourceManager() {
        if (resourceManager == null) {
            resourceManager = new ResourceManagerImpl();
        }

        return resourceManager;
    }

    public Project getProject(String id) {
        Session session = getHibernateSession();
        Project ret = (Project) session.createQuery("from StoredProject p where id = '" + id + "'").uniqueResult();

        return ret;
    }

    public List<Project> getProjects() {
        Session session = getHibernateSession();
        List<Project> list = session
                .createQuery("from StoredProject p where id != '" + Project.ALL_PROJECT_ID + "' order by name")
                .list();

        return list;
    }

    public List<Project> getRootProjects() {
        return getRootProjects(false);
    }

    public List<Project> getRootProjects(boolean withDisabled) {
        Session session = getHibernateSession();

        return getProjectCriteria(session, withDisabled, null).list();
    }

    public List<Project> getActiveRootProjects() {
        Session session = getHibernateSession();

        if (activeProjectIds.size() == 0) {
            return new ArrayList<Project>();
        }
        return getProjectCriteria(session, false, activeProjectIds).list();
    }

    public List<Project> getRecentRootProjects(User user) {
        Session session = getHibernateSession();
        if (user == null || user.getUsername().equals("anonymous")) {
            return new ArrayList<Project>();
        }

        Set<String> recentIds = recentProjectIds.get(user.getUsername());
        if (recentIds == null || recentIds.size() == 0) {
            return new ArrayList<Project>();
        }
        return getProjectCriteria(session, false, recentIds).list();
    }

    protected Criteria getProjectCriteria(Session session, boolean disabled, Set<String> ids) {
        Criteria criteria = session.createCriteria(Project.class);
        criteria.add(Restrictions.not(Restrictions.eq("id", Project.ALL_PROJECT_ID)));
        criteria.add(Restrictions.isNull("parent"));

        if (!disabled) {
            criteria.add(Restrictions.or(Restrictions.isNull("disabled"), Restrictions.eq("disabled", false)));
        }

        if (ids != null) {
            criteria.add(Restrictions.in("id", ids));
        }

        criteria.addOrder(Order.asc("name"));
        return criteria;
    }

    public List<Event> getEvents(Date start) {
        Session session = getHibernateSession();
        Query q = session.createQuery("from StoredEvent e where time >= :start order by time desc");
        q.setDate("start", start);

        q.setReadOnly(true);
        return (List<Event>) q.list();
    }

    public List<Event> getEvents(Date start, Date end) {
        Session session = getHibernateSession();
        Query q = session.createQuery("from StoredEvent e where time >= :start and time < :end order by time desc");
        q.setDate("start", start);
        q.setDate("end", end);

        q.setReadOnly(true);
        return (List<Event>) q.list();
    }

    public List<Event> getEvents(Application app, Date start, Date end) {
        Session session = getHibernateSession();
        Query q = session.createQuery(
                "from StoredEvent e where applicationId = :appId and time >= :start and time < :end order by time desc");
        q.setString("appId", app.getApplicationId());
        q.setTimestamp("start", start);
        q.setTimestamp("end", end);

        q.setReadOnly(true);
        return (List<Event>) q.list();
    }

    public List<Event> getEventsForProject(Project project, Date start, Date end) {
        return doGetEventsForProject(project, null, start, end, false);
    }

    public List<Event> getEventsForProject(Project project, Application app, Date start, Date end) {
        return doGetEventsForProject(project, app, start, end, false);
    }

    public List<Event> getEventsForProjectTree(Project project, Date start, Date end) {
        return doGetEventsForProject(project, null, start, end, true);
    }

    public List<Event> getEventsForProjectTree(Project project, Application app, Date start, Date end) {
        return doGetEventsForProject(project, app, start, end, true);
    }

    private List<Event> doGetEventsForProject(Project project, Application app, Date start, Date end,
            boolean tree) {
        String query = "from StoredEvent e where project.id = :pid";
        if (tree) {
            query = "from StoredEvent e where project.id in (:pids)";
        }

        if (app != null) {
            query += " and applicationId = :appId";
        }

        if (start != null) {
            query += " and time >= :start and time < :end";
        }
        query += " order by time desc";

        Session session = getHibernateSession();
        Transaction tx = session.beginTransaction();
        Query q = session.createQuery(query);

        if (tree) {
            project = (Project) session.merge(project);
            List<String> projects = new LinkedList<String>();
            doListProjectIds(project, projects);
            q.setParameterList("pids", projects);
        } else {
            q.setString("pid", project.getId());
        }

        if (app != null) {
            q.setString("appId", app.getApplicationId());
        }

        if (start != null) {
            q.setTimestamp("start", start);
            q.setTimestamp("end", end);
        }

        q.setReadOnly(true);
        List<Event> list = q.list();
        tx.commit();

        return list;
    }

    private void doListProjectIds(Project project, List<String> projects) {
        projects.add(project.getId());
        for (Project child : project.getChildProjects()) {
            doListProjectIds(child, projects);
        }
    }

    public List<Event> getEventsForUser(User user, Date start, Date stop) {
        Session session = getHibernateSession();
        Transaction tx = session.beginTransaction();

        Query q = session.createQuery("from StoredEvent e where time >= :start and time < :end and "
                + "(username = :username or username like :emailLike or username like :nameLike) order by time desc");
        q.setTimestamp("start", start);
        q.setTimestamp("end", stop);
        q.setString("username", user.getUsername());

        if (!StringUtil.isEmpty(user.getEmail())) {
            q.setString("emailLike", "%<" + user.getEmail() + ">");
        } else {
            // a silly fallback for now
            q.setString("emailLike", user.getUsername());
        }

        if (!StringUtil.isEmpty(user.getFullname())) {
            q.setString("nameLike", user.getFullname() + " <%");
        } else {
            // a silly fallback for now
            q.setString("nameLike", user.getUsername());
        }

        q.setReadOnly(true);
        List<Event> list = q.list();
        tx.commit();

        return list;
    }

    public void addProject(final Project proj) {
        save(proj);

        if (proj.equals(StoredProject.getDefault())) {
            return;
        }
        Manager.getInstance().fireProjectAdded(proj);

        addChildProjects(proj);
    }

    private void addChildProjects(Project proj) {
        for (Project child : proj.getChildProjects()) {
            save(child);
            Manager.getInstance().fireProjectAdded(child);

            addChildProjects(child);
        }
    }

    public void addEvent(Event event) {
        addActiveProjectForEvent(event);
        addRecentProjectForEvent(event);

        save(event);
    }

    public Object save(Object o) {
        Session session = getHibernateSession();
        Transaction tx = session.beginTransaction();
        Object ret = session.save(o);
        tx.commit();

        return ret;
    }

    public void update(Object o) {
        Session session = getHibernateSession();
        Transaction tx = session.beginTransaction();
        session.saveOrUpdate(o);
        tx.commit();
    }

    public Object merge(Object o) {
        Session session = getHibernateSession();
        Transaction tx = session.beginTransaction();
        Object ret = session.merge(o);
        tx.commit();

        return ret;
    }

    public void delete(Object o) {
        Session session = getHibernateSession();
        Transaction tx = session.beginTransaction();
        session.delete(o);
        tx.commit();
    }
}