com.enonic.cms.core.vacuum.VacuumServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.enonic.cms.core.vacuum.VacuumServiceImpl.java

Source

/*
 * Copyright 2000-2013 Enonic AS
 * http://www.enonic.com/license
 */

package com.enonic.cms.core.vacuum;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.enonic.cms.framework.blob.gc.GarbageCollector;

import com.enonic.cms.core.security.SecurityService;
import com.enonic.cms.core.security.user.User;
import com.enonic.cms.core.security.userstore.MemberOfResolver;
import com.enonic.cms.store.support.ConnectionFactory;

@Component
public class VacuumServiceImpl implements VacuumService {
    private static final int BATCH_SIZE = 10;

    private static final String VACUUM_READ_LOGS_SQL = "DELETE FROM tLogEntry WHERE len_lTypeKey = 7";

    @Autowired
    protected GarbageCollector garbageCollector;

    @Autowired
    protected ConnectionFactory connectionFactory;

    @Autowired
    protected SecurityService securityService;

    @Autowired
    protected MemberOfResolver memberOfResolver;

    private ProgressInfo progressInfo = new ProgressInfo();

    private final Map<String, List<Integer>> queryCache = new HashMap<String, List<Integer>>();

    /**
     * Clean read logs.
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void cleanReadLogs() {
        if (progressInfo.isInProgress() || !isAdmin()) {
            return;
        }

        try {
            startProgress("Cleaning read logs...");

            final Connection conn = connectionFactory.getConnection(true);

            setProgress("Vacuum read logs...", 5);

            vacuumReadLogs(conn);
        } catch (final Exception e) {
            setProgress("Failed to clean read logs: " + e.getMessage(), 100);
            progressInfo.setInProgress(false);

            throw new RuntimeException("Failed to clean read logs", e);
        } finally {
            finishProgress();
        }
    }

    /**
     * Clean unused content.
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, timeout = 3600)
    public void cleanUnusedContent() {
        if (progressInfo.isInProgress() || !isAdmin()) {
            return;
        }

        try {
            startProgress("Cleaning unused content...");

            final Connection conn = connectionFactory.getConnection(true);

            setProgress("Vacuum binaries...", 5);
            vacuumBinaries(conn);

            setProgress("Vacuum contents...", 20);
            vacuumContents(conn);

            setProgress("Vacuum categories...", 40);
            vacuumCategories(conn);

            setProgress("Vacuum archives...", 60);
            vacuumArchives(conn);

            setProgress("Vacuum blob store...", 80);
            vacuumBlobStore();

        } catch (final Exception e) {
            setProgress("Failed to clean unused content: " + e.getMessage(), 100);
            progressInfo.setInProgress(false);

            throw new RuntimeException("Failed to clean unused content", e);
        } finally {
            finishProgress();

            queryCache.clear();
        }
    }

    /**
     * returns progress info about either Clean unused content or Clean read logs.
     */
    public ProgressInfo getProgressInfo() {
        if (!isAdmin()) {
            return ProgressInfo.NONE;
        }

        return progressInfo;
    }

    private void startProgress(final String logLine) {
        setProgress(logLine, 0);
        progressInfo.setInProgress(true);
    }

    private void setProgress(final String logLine, final int percent) {
        progressInfo.setLogLine(logLine);
        progressInfo.setPercent(percent);
    }

    private void finishProgress() {
        if (progressInfo.isInProgress()) {
            setProgress("Finished. Last job was executed at " + new Date().toString(), 100);
            progressInfo.setInProgress(false);
        }
    }

    private boolean isAdmin() {
        final User user = securityService.getLoggedInAdminConsoleUser();
        return memberOfResolver.hasEnterpriseAdminPowers(user.getKey());
    }

    /**
     * Vacuum binaries.
     */
    private void vacuumBinaries(final Connection conn) throws Exception {
        executeStatements(conn, VacuumContentSQL.VACUUM_BINARIES_STATEMENTS);
    }

    /**
     * Vacuum contents.
     */
    private void vacuumContents(final Connection conn) throws Exception {
        executeStatements(conn, VacuumContentSQL.VACUUM_CONTENT_STATEMENTS);
    }

    /**
     * Vacuum categories.
     */
    private void vacuumCategories(final Connection conn) throws Exception {
        executeStatements(conn, VacuumContentSQL.VACUUM_CATEGORIES_STATEMENTS);
    }

    /**
     * Vacuum arvhives.
     */
    private void vacuumArchives(final Connection conn) throws Exception {
        executeStatements(conn, VacuumContentSQL.VACUUM_ARCHIVES_STATEMENTS);
    }

    /**
     * Vacuum read logs.
     */
    private void vacuumReadLogs(final Connection conn) throws Exception {
        executeStatements(conn, new String[] { VACUUM_READ_LOGS_SQL });
    }

    private void vacuumBlobStore() {
        this.garbageCollector.process();
    }

    /**
     * Execute a list of statements.
     */
    private void executeStatements(final Connection conn, final String[] sqlList) throws Exception {
        for (final String sql : sqlList) {
            executeStatementBatch(conn, sql);
        }
    }

    /**
     * Execute statement.
     */
    private void executeStatement(final Connection conn, final String sql) throws Exception {
        Statement stmt = null;

        try {
            stmt = conn.createStatement();
            stmt.execute(sql);
        } finally {
            JdbcUtils.closeStatement(stmt);
        }
    }

    /**
     * Execute statement trying do it in batch.
     */
    private void executeStatementBatch(final Connection conn, final String sql) throws Exception {
        final Pattern pattern = Pattern.compile("DELETE FROM (\\w+) WHERE (\\w+) IN \\((.*)\\)");
        final Matcher matcher = pattern.matcher(sql);

        if (matcher.matches()) {
            final String table = matcher.group(1);
            final String column = matcher.group(2);
            final String select = matcher.group(3);

            final List<Integer> ids = readIds(conn, select);
            deleteIdsInTableBatch(conn, ids, column, table);
        } else {
            // WHERE NOT IN query or some unknown ... -> just execute SQL.
            executeStatement(conn, sql);
        }
    }

    /**
     * gets array of primary keys that are to delete. used query cache.
     */
    private List<Integer> readIds(final Connection conn, final String select) throws SQLException {
        List<Integer> ids = queryCache.get(select);

        if (ids == null) {
            ids = readIdsFromDB(conn, select);

            queryCache.put(select, ids);
        }

        return ids;
    }

    /**
     * gets array of primary keys that are to delete. read from database
     */
    private List<Integer> readIdsFromDB(final Connection conn, final String select) throws SQLException {
        final List<Integer> ids = new ArrayList<Integer>();

        Statement stmt = null;
        ResultSet rs = null;

        try {
            stmt = conn.createStatement();

            rs = stmt.executeQuery(select);

            while (rs.next()) {
                ids.add(rs.getInt(1));
            }

        } finally {
            JdbcUtils.closeResultSet(rs);
            JdbcUtils.closeStatement(stmt);
        }

        return ids;
    }

    /**
     * splits delete to batch
     */
    private void deleteIdsInTableBatch(final Connection conn, final List<Integer> ids, final String column,
            final String table) throws Exception {
        int fromIndex, length;

        for (fromIndex = 0, length = ids.size(); fromIndex < length; fromIndex += BATCH_SIZE) {
            final int toIndex = fromIndex + BATCH_SIZE < length ? fromIndex + BATCH_SIZE : length;
            deleteIdsInTable(conn, ids.subList(fromIndex, toIndex), column, table);
        }

    }

    /**
     * deletes from table by idsds
     */
    private void deleteIdsInTable(final Connection conn, final List<Integer> ids, final String column,
            final String table) throws Exception {
        final String join = StringUtils.join(ids, ",");

        final String sql = "DELETE FROM " + table + " WHERE " + column + " IN (" + join + ")";

        executeStatement(conn, sql);
    }
}