Package org.jasig.portal.utils

Source Code of org.jasig.portal.utils.HibernateStyleCounterStore

/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you 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.
*/
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA  02110-1301  USA
*/

package org.jasig.portal.utils;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.id.enhanced.AccessCallback;
import org.hibernate.id.enhanced.Optimizer;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.type.IntegerType;
import org.hibernate.type.Type;
import org.jasig.portal.jpa.BasePortalJpaDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionOperations;

/**
* Mostly cloned from {@link TableGenerator}
*
* @author Eric Dalquist
* @version $Revision$
*/
@Repository("counterStore")
public class HibernateStyleCounterStore implements ICounterStore {
    private static final String SELECT_QUERY = "SELECT SEQUENCE_VALUE FROM UP_SEQUENCE WHERE SEQUENCE_NAME=?";
    private static final String UPDATE_QUERY = "UPDATE UP_SEQUENCE SET SEQUENCE_VALUE=? WHERE SEQUENCE_NAME=? AND SEQUENCE_VALUE=?";
    private static final String INSERT_QUERY = "INSERT INTO UP_SEQUENCE (SEQUENCE_NAME, SEQUENCE_VALUE) VALUES (?, ?)";

    private final Type identifierType = IntegerType.INSTANCE;

    private final ConcurrentMap<String, Callable<Optimizer>> counterOptimizers = new ConcurrentHashMap<String, Callable<Optimizer>>();
    private TransactionOperations transactionOperations;
    private JdbcOperations jdbcOperations;
    private int incrementSize = 50;
    private int initialValue = 10;
   
    @Autowired
    public void setTransactionOperations(@Qualifier(BasePortalJpaDao.PERSISTENCE_UNIT_NAME) TransactionOperations transactionOperations) {
        this.transactionOperations = transactionOperations;
    }

    @Autowired
    public void setJdbcOperations(@Qualifier(BasePortalJpaDao.PERSISTENCE_UNIT_NAME) JdbcOperations jdbcOperations) {
        this.jdbcOperations = jdbcOperations;
    }

    @Value("${org.jasig.portal.utils.HibernateStyleCounterStore.incrementSize:50}")
    public void setIncrementSize(int incrementSize) {
        this.incrementSize = incrementSize;
    }

    @Value("${org.jasig.portal.utils.HibernateStyleCounterStore.initialValue:10}")
    public void setInitialValue(int initialValue) {
        this.initialValue = initialValue;
    }

    private Optimizer getCounterOptimizer(String counterName) {
        Callable<Optimizer> optimizer = counterOptimizers.get(counterName);
        if (optimizer == null) {
            optimizer = new Callable<Optimizer>() {
                private volatile Optimizer optimizer;
               
                @Override
                public Optimizer call() throws Exception {
                    Optimizer o = optimizer;
                    if (o != null) {
                        return o;
                    }
                   
                    synchronized (this) {
                        o = optimizer;
                        if (o != null) {
                            return o;
                        }
                   
                        o = OptimizerFactory.buildOptimizer(
                            OptimizerFactory.StandardOptimizerDescriptor.POOLED.getExternalName(),
                            identifierType.getReturnedClass(),
                            incrementSize,
                            initialValue);
                        this.optimizer = o;
                    }
                   
                    return o;
                }
            };

            optimizer = ConcurrentMapUtils.putIfAbsent(counterOptimizers, counterName, optimizer);
        }

        try {
            return optimizer.call();
        }
        catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new RuntimeException(e);
        }
    }

    @Override
    public int getNextId(String counterName) {
        final int id;
        final Optimizer counterOptimizer = this.getCounterOptimizer(counterName);
        synchronized (counterOptimizer) {
            id = getNextIdInternal(counterOptimizer, counterName);
        }
       
        return id;
    }

    private int getNextIdInternal(final Optimizer optimizer, final String counterName) {
        return (Integer) optimizer.generate(new AccessCallback() {
            @Override
            public IntegralDataTypeHolder getNextValue() {
                return transactionOperations.execute(new TransactionCallback<IntegralDataTypeHolder>() {
                    @Override
                    public IntegralDataTypeHolder doInTransaction(TransactionStatus status) {
                        final IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder(identifierType.getReturnedClass());
                        int rows;
                        do {
                            //Try and load the current value, returns true if the exepected row exists, null otherwise
                            final boolean selected = jdbcOperations.query(SELECT_QUERY, new ResultSetExtractor<Boolean>() {
                                        @Override
                                        public Boolean extractData(ResultSet rs) throws SQLException, DataAccessException {
                                            if (rs.next()) {
                                                value.initialize(rs, 1);
                                                return true;
                                            }
                                            return false;
                                        }
                                }, counterName);
                           
                            //No row exists for the counter, insert it
                            if (!selected) {
                                value.initialize(initialValue);
                               
                                jdbcOperations.update(INSERT_QUERY, new PreparedStatementSetter() {
                                    @Override
                                    public void setValues(PreparedStatement ps) throws SQLException {
                                        ps.setString(1, counterName);
                                        value.bind(ps, 2);
                                    }
                                });
                            }
                           
                            //Increment the counter row value
                            final IntegralDataTypeHolder updateValue = value.copy();
                            if (optimizer.applyIncrementSizeToSourceValues()) {
                                updateValue.add(incrementSize);
                            }
                            else {
                                updateValue.increment();
                            }
                           
                            //Update the counter row, if rows returns 0 the update failed due to a race condition, it will be retried
                            rows = jdbcOperations.update(UPDATE_QUERY, new PreparedStatementSetter() {
                                @Override
                                public void setValues(PreparedStatement ps) throws SQLException {
                                    updateValue.bind(ps, 1);
                                    ps.setString(2, counterName);
                                    value.bind(ps, 3);
                                }
                            });
                        } while (rows == 0);

                        return value;
                    }
                });
            }
        });
    }
}
TOP

Related Classes of org.jasig.portal.utils.HibernateStyleCounterStore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.