iudex.da.ContentUpdater.java Source code

Java tutorial

Introduction

Here is the source code for iudex.da.ContentUpdater.java

Source

/*
 * Copyright (c) 2008-2012 David Kellum
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package iudex.da;

import iudex.core.ContentKeys;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.sql.DataSource;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;

import com.gravitext.htmap.UniMap;

public class ContentUpdater extends ContentWriter {
    public ContentUpdater(DataSource source, ContentMapper mapper, Transformer transformer) {
        super(source, mapper);
        _transformer = transformer;
    }

    /**
     * Update first any content REFERENCES and then the content itself.
     */
    public void update(UniMap content) throws SQLException {
        Connection conn = dataSource().getConnection();
        try {
            conn.setAutoCommit(false);
            conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
            //FIXME: Correct isolation?

            List<UniMap> refs = content.get(ContentKeys.REFERENCES);
            if (refs != null) {
                update(refs, conn);
            }

            UniMap referer = content.get(ContentKeys.REFERER);
            if (referer != null) {
                //FIXME: Really sufficient as same path as content?
                update(referer, conn);
            }

            update(content, conn);

            conn.commit();
        } finally {
            if (conn != null)
                conn.close();
        }
    }

    public void update(List<UniMap> references) throws SQLException {
        Connection conn = dataSource().getConnection();
        try {
            conn.setAutoCommit(false);
            conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

            update(references, conn);

            conn.commit();
        } finally {
            if (conn != null)
                conn.close();
        }
    }

    protected void update(UniMap content, Connection conn) throws SQLException {
        final StringBuilder qb = new StringBuilder(256);
        qb.append("SELECT ");
        mapper().appendFieldNames(qb);
        qb.append(" FROM urls WHERE uhash = ? FOR UPDATE;");

        UpdateQueryRunner runner = new UpdateQueryRunner();
        int update = runner.query(conn, qb.toString(), new OneUpdateHandler(content),
                content.get(ContentKeys.URL).uhash());

        if (update == 0) {
            UniMap out = _transformer.transformContent(content, null);
            if (out != null)
                write(out, conn);
        }
    }

    protected void update(List<UniMap> references, Connection conn) throws SQLException {
        final HashMap<String, UniMap> uhashes = new HashMap<String, UniMap>(references.size());
        final String qry = formatSelect(references, uhashes);
        final UpdateQueryRunner runner = new UpdateQueryRunner();
        runner.query(conn, qry, new RefUpdateHandler(uhashes));

        final ArrayList<UniMap> remains = new ArrayList<UniMap>(uhashes.size());
        for (UniMap rem : uhashes.values()) {
            UniMap out = _transformer.transformReference(rem, null);
            if (out != null)
                remains.add(out);
        }
        if (remains.size() > 0)
            write(remains, conn);
    }

    private String formatSelect(final List<UniMap> references, final HashMap<String, UniMap> uhashes) {
        final StringBuilder qb = new StringBuilder(512);
        qb.append("SELECT ");
        mapper().appendFieldNames(qb);
        qb.append(" FROM urls WHERE uhash IN (");
        boolean first = true;
        for (UniMap r : references) {
            if (first)
                first = false;
            else
                qb.append(", ");

            final String uhash = r.get(ContentKeys.URL).uhash();
            uhashes.put(uhash, r);

            qb.append('\'');
            qb.append(uhash);
            qb.append('\'');
        }
        qb.append(") FOR UPDATE;");

        return qb.toString();
    }

    private final class RefUpdateHandler implements ResultSetHandler<Object> {
        public RefUpdateHandler(HashMap<String, UniMap> hashes) {
            _hashes = hashes;
        }

        @Override
        public Object handle(ResultSet rs) throws SQLException {
            while (rs.next()) {
                final UniMap in = mapper().fromResultSet(rs);
                final UniMap ref = _hashes.remove(rs.getString("uhash"));

                UniMap out = _transformer.transformReference(ref, in);

                if ((out != null) && mapper().update(rs, in, out)) {
                    rs.updateRow();
                }
            }
            return null;
        }

        private HashMap<String, UniMap> _hashes;
    }

    private final class OneUpdateHandler implements ResultSetHandler<Integer> {
        public OneUpdateHandler(UniMap content) {
            _content = content;
        }

        @Override
        public Integer handle(ResultSet rs) throws SQLException {
            if (rs.next()) {
                final UniMap in = mapper().fromResultSet(rs);

                UniMap out = _transformer.transformContent(_content, in);

                if ((out != null) && mapper().update(rs, in, out)) {
                    rs.updateRow();
                }
                return 1;
            }
            if (rs.next()) {
                throw new IllegalStateException(
                        "Multiple url rows returned for uhash=" + _content.get(ContentKeys.URL).uhash());
            }
            return 0;
        }

        private UniMap _content;
    }

    private static final class UpdateQueryRunner extends QueryRunner {
        @Override
        protected PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException {
            return conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
        }
    }

    private final Transformer _transformer;
}