com.bt.aloha.dao.CallInfoDaoImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.bt.aloha.dao.CallInfoDaoImpl.java

Source

/*
 * Aloha Open Source SIP Application Server- https://trac.osmosoft.com/Aloha
 *
 * Copyright (c) 2008, British Telecommunications plc. All rights reserved.
 *
 * This library 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.0 of the License, or (at your option) any later
 * version.
 *
 * This library 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 library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package com.bt.aloha.dao;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.sdp.MediaDescription;
import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.SqlLobValue;

import com.bt.aloha.call.state.CallInfo;
import com.bt.aloha.call.state.CallLegCausingTermination;
import com.bt.aloha.call.state.CallLegConnectionState;
import com.bt.aloha.call.state.CallState;
import com.bt.aloha.call.state.CallTerminationCause;
import com.bt.aloha.call.state.MediaNegotiationMethod;
import com.bt.aloha.call.state.MediaNegotiationState;
import com.bt.aloha.call.state.PendingCallReinvite;
import com.bt.aloha.callleg.AutoTerminateAction;
import com.bt.aloha.util.ConcurrentUpdateException;
import com.bt.aloha.util.ObjectSerialiser;

public class CallInfoDaoImpl extends DaoBase implements CallInfoDao {

    private static final String NO_CALL_FOUND_FOR_DIALOG_S_RETURNING_NULL = "no call found for dialog %s, returning null";

    private static final String INFO_S_ALREADY_EXISTS_IN_DATABASE_USE_REPLACE_DIALOG_INSTEAD = "Info %s already exists in database, use replaceDialog instead";

    private static final String COUNT_SQL = "select count(callId) from callInfo";

    private static final String COUNT_SQL_BY_STATE = COUNT_SQL + " where callState=?";

    private static final int V_ID_POS = 6;

    private static final String COLUMNS = "callId, " + "classname, " + "simpleSipBeanId, " + "createTime, "
            + "startTime, " + "endTime, " + "object_version, " + "last_use_time, " + "callState, "
            + "firstDialogId, " + "secondDialogId, " + "firstCallLegConnectionState, "
            + "secondCallLegConnectionState, " + "mediaNegotiationState, " + "mediaNegotiationMethod, "
            + "maxDurationInMinutes, " + "autoTerminate, " + "callTerminationCause, "
            + "callLegCausingTermination, " + "pendingCallReinvite_dialogId, "
            + "pendingCallReinvite_remoteContact, " + "pendingCallReinvite_offerInOkResponse, "
            + "pendingCallReinvite_autoTerminate, " + "pendingCallReinvite_mediaDescription, "
            + "pendingCallReinvite_applicationData, housekeepingForced";

    private static final String UPDATE_SQL = "update callinfo set classname=?," + "simpleSipBeanId=?,"
            + "createTime=?," + "startTime=?," + "endTime=?," + "object_version=?," + "last_use_time=?,"
            + "callState=?," + "firstDialogId=?," + "secondDialogId=?," + "firstCallLegConnectionState=?,"
            + "secondCallLegConnectionState=?," + "mediaNegotiationState=?," + "mediaNegotiationMethod=?,"
            + "maxDurationInMinutes=?," + "autoTerminate=?," + "callTerminationCause=?,"
            + "callLegCausingTermination=?," + "pendingCallReinvite_dialogId=?,"
            + "pendingCallReinvite_remoteContact=?," + "pendingCallReinvite_offerInOkResponse=?,"
            + "pendingCallReinvite_autoTerminate=?," + "pendingCallReinvite_mediaDescription=?,"
            + "pendingCallReinvite_applicationData=?," + "housekeepingForced=?"
            + "where callId=? and object_version=?";

    private static final String INSERT_SQL = "insert into callInfo (" + COLUMNS + ") "
            + "values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

    private static final String SELECT_COLUMNS = "select " + COLUMNS + " from callInfo";

    private static final String SELECT_BY_ID_SQL = SELECT_COLUMNS + " where callId=?";

    private static final String SELECT_WHERE_DIALOG_ID_SQL = SELECT_COLUMNS
            + " where (firstDialogId=? or secondDialogId=?) and createTime = (select max(createTime) from callInfo where firstDialogId=? or secondDialogId=?)";
    private static final String SELECT_WHERE_DIALOG_ID_IGNORE_CALL_ID_SQL = SELECT_COLUMNS
            + " where callId != ? and (firstDialogId=? or secondDialogId=?) and createTime = (select max(createTime) from callInfo where callId != ? and (firstDialogId=? or secondDialogId=?))";

    private static final String DELETE_SQL = "delete from callinfo where callid = ?";

    private static final int[] INSERT_TYPES = new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.BIGINT,
            Types.BIGINT, Types.BIGINT, Types.VARCHAR, Types.BIGINT, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
            Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.BIGINT, Types.VARCHAR, Types.VARCHAR,
            Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.BOOLEAN, Types.BOOLEAN, Types.BLOB, Types.VARCHAR,
            Types.BOOLEAN, };

    private static final int[] UPDATE_TYPES = new int[] { Types.VARCHAR, Types.VARCHAR, Types.BIGINT, Types.BIGINT,
            Types.BIGINT, Types.VARCHAR, Types.BIGINT, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
            Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.BIGINT, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
            Types.VARCHAR, Types.VARCHAR, Types.BOOLEAN, Types.BOOLEAN, Types.BLOB, Types.VARCHAR, Types.BOOLEAN,
            Types.VARCHAR, Types.VARCHAR, };

    private static final String CALLINFO_DELETE_BY_HOUSEKEEPER_FLAGS_SQL = "delete from callinfo where (callstate = 'Terminated' or housekeepingForced = 'True') and last_use_time < ?";
    private static final String CALLINFO_FIND_BY_HOUSEKEEPER_FLAGS_SQL = "select callId from callinfo where (callstate = 'Terminated' or housekeepingForced = 'True') and last_use_time < ?";
    private static final String CALLINFO_UPDATE_BY_HOUSEKEEPER_FLAGS_SQL = SELECT_COLUMNS
            + " where callstate != 'Terminated' and last_use_time < ?";
    private static final String SELECT_CONNECTED_MAX_DURATION_CALLS_SQL = SELECT_COLUMNS
            + " where callstate = 'Connected' and maxDurationInMinutes > 0";

    private static final String SELECT_ALL_SQL = "select * from callinfo";

    private Log log = LogFactory.getLog(CallInfoDaoImpl.class);
    private ObjectSerialiser objectSerialiser;

    public CallInfoDaoImpl(DataSource aDataSource) {
        super(aDataSource);
        this.objectSerialiser = new ObjectSerialiser();
    }

    public void create(CallInfo callInfo) {
        log.debug(String.format("create(%s)", callInfo.getId()));
        try {
            List<Object> paramList = createParams(callInfo);
            Object[] params = paramList.toArray();
            getJdbcTemplate().update(INSERT_SQL, params, INSERT_TYPES);
        } catch (DataIntegrityViolationException e) {
            throw new IllegalArgumentException(
                    String.format(INFO_S_ALREADY_EXISTS_IN_DATABASE_USE_REPLACE_DIALOG_INSTEAD, callInfo.getId()),
                    e);
        }
    }

    private List<Object> createParams(CallInfo callInfo) {
        List<Object> result = new Vector<Object>();
        result.add(callInfo.getId());
        result.add(callInfo.getClass().getName());
        result.add(callInfo.getSimpleSipBeanId());
        result.add(callInfo.getCreateTime());
        result.add(callInfo.getStartTime());
        result.add(callInfo.getEndTime());
        result.add(callInfo.getVersionId());
        result.add(callInfo.getLastUsedTime());
        result.add(readEnum(callInfo.getCallState()));
        result.add(callInfo.getFirstDialogId());
        result.add(callInfo.getSecondDialogId());
        result.add(readEnum(callInfo.getFirstCallLegConnectionState()));
        result.add(readEnum(callInfo.getSecondCallLegConnectionState()));
        result.add(readEnum(callInfo.getMediaNegotiationState()));
        result.add(readEnum(callInfo.getMediaNegotiationMethod()));
        result.add(callInfo.getMaxDurationInMinutes());
        result.add(readEnum(callInfo.getAutoTerminate()));
        result.add(readEnum(callInfo.getCallTerminationCause()));
        result.add(readEnum(callInfo.getCallLegCausingTermination()));
        result.add(
                null == callInfo.getPendingCallReinvite() ? null : callInfo.getPendingCallReinvite().getDialogId());
        result.add(null == callInfo.getPendingCallReinvite() ? null
                : callInfo.getPendingCallReinvite().getRemoteContact());
        result.add(null == callInfo.getPendingCallReinvite() ? false
                : callInfo.getPendingCallReinvite().getOfferInOkResponse());
        result.add(null == callInfo.getPendingCallReinvite() ? false
                : callInfo.getPendingCallReinvite().getAutoTerminate());
        result.add(null == callInfo.getPendingCallReinvite() ? null
                : null == callInfo.getPendingCallReinvite().getMediaDescription() ? null
                        : new SqlLobValue(objectSerialiser
                                .serialise(callInfo.getPendingCallReinvite().getMediaDescription())));
        result.add(null == callInfo.getPendingCallReinvite() ? null
                : callInfo.getPendingCallReinvite().getApplicationData());
        result.add(callInfo.isHousekeepForced());
        return result;
    }

    private Object readEnum(Object e) {
        if (null == e)
            return null;
        return e.toString();
    }

    class CallInfoRowMapper implements RowMapper {

        private static final String ERROR_CREATING_CALL_INFO = "Error creating CallInfo";

        public CallInfoRowMapper() {
        }

        @SuppressWarnings("unchecked")
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            String classname = rs.getString("classname");
            String creatingBeanName = rs.getString("simpleSipBeanId");
            String aCallId = rs.getString("callId");
            String theFirstDialogId = rs.getString("firstDialogId");
            String theSecondDialogId = rs.getString("secondDialogId");
            String autoTerminateString = rs.getString("autoTerminate");
            AutoTerminateAction aAutoTerminateAction = autoTerminateString == null ? null
                    : AutoTerminateAction.valueOf(autoTerminateString);
            long theMaxDurationInMinutes = rs.getLong("maxDurationInMinutes");

            CallInfo info = null;
            // TODO: test around the exception handling cases
            try {
                Class c = Class.forName(classname);
                Constructor constructor = c.getConstructor(new Class[] { String.class, String.class, String.class,
                        String.class, AutoTerminateAction.class, long.class });
                info = (CallInfo) constructor.newInstance(creatingBeanName, aCallId, theFirstDialogId,
                        theSecondDialogId, aAutoTerminateAction, theMaxDurationInMinutes);
                log.debug(String.format("created %s", info.getClass().getName()));
            } catch (ClassNotFoundException e) {
                log.error(ERROR_CREATING_CALL_INFO, e);
                throw new RuntimeException(e);
            } catch (InstantiationException e) {
                log.error(ERROR_CREATING_CALL_INFO, e);
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                log.error(ERROR_CREATING_CALL_INFO, e);
                throw new RuntimeException(e);
            } catch (SecurityException e) {
                log.error(ERROR_CREATING_CALL_INFO, e);
                throw new RuntimeException(e);
            } catch (NoSuchMethodException e) {
                log.error(ERROR_CREATING_CALL_INFO, e);
                throw new RuntimeException(e);
            } catch (IllegalArgumentException e) {
                log.error(ERROR_CREATING_CALL_INFO, e);
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                log.error(ERROR_CREATING_CALL_INFO, e);
                throw new RuntimeException(e);
            }

            info.setVersionId(rs.getString("object_version"));
            long createTime = rs.getLong("createTime");
            info.setCreateTime(createTime);
            long startTime = rs.getLong("startTime");
            info.setStartTime(startTime);
            long endTime = rs.getLong("endTime");
            info.setEndTime(endTime);
            String callStateString = rs.getString("callState");
            info.setCallState(callStateString == null ? null : CallState.valueOf(callStateString));
            info.setLastUsedTime(rs.getLong("last_use_time"));
            String firstCallLegConnectionStateString = rs.getString("firstCallLegConnectionState");
            info.setCallLegConnectionState(theFirstDialogId, firstCallLegConnectionStateString == null ? null
                    : CallLegConnectionState.valueOf(firstCallLegConnectionStateString));
            String secondCallLegConnectionState = rs.getString("secondCallLegConnectionState");
            info.setCallLegConnectionState(theSecondDialogId, secondCallLegConnectionState == null ? null
                    : CallLegConnectionState.valueOf(secondCallLegConnectionState));
            String mediaNegStateString = rs.getString("mediaNegotiationState");
            info.setMediaNegotiationState(
                    mediaNegStateString == null ? null : MediaNegotiationState.valueOf(mediaNegStateString));
            String mediaNegMtString = rs.getString("mediaNegotiationMethod");
            info.setMediaNegotiationMethod(
                    mediaNegMtString == null ? null : MediaNegotiationMethod.valueOf(mediaNegMtString));
            String callTermCauseString = rs.getString("callTerminationCause");
            String callLegCausingTermString = rs.getString("callLegCausingTermination");
            info.setCallTerminationCause(
                    callTermCauseString == null ? null : CallTerminationCause.valueOf(callTermCauseString),
                    callLegCausingTermString == null ? null
                            : CallLegCausingTermination.valueOf(callLegCausingTermString));
            String aDialogId = rs.getString("pendingCallReinvite_dialogId");
            MediaDescription aMediaDescription = null;
            byte[] mdba = rs.getBytes("pendingCallReinvite_mediaDescription");
            if (mdba != null) {
                aMediaDescription = (MediaDescription) objectSerialiser.deserialise(mdba);
            }
            String theRemoteContact = rs.getString("pendingCallReinvite_remoteContact");
            String aApplicationData = rs.getString("pendingCallReinvite_applicationData");

            Boolean aOfferInOkResponse = rs.getBoolean("pendingCallReinvite_offerInOkResponse");
            Boolean aPcrAutoTerminate = rs.getBoolean("pendingCallReinvite_autoTerminate");
            PendingCallReinvite pcr = new PendingCallReinvite(aDialogId, aMediaDescription, aPcrAutoTerminate,
                    theRemoteContact, aApplicationData, aOfferInOkResponse);
            info.setPendingCallReinvite(pcr);
            info.setHousekeepForced(rs.getBoolean("housekeepingForced"));

            return info;
        }
    }

    public CallInfo read(String callId) {
        CallInfo callInfo = (CallInfo) getJdbcTemplate().queryForObject(SELECT_BY_ID_SQL, new Object[] { callId },
                new CallInfoRowMapper());
        return callInfo;
    }

    public void delete(String callId) {
        Object[] params = new Object[] { callId };
        int[] types = new int[] { Types.VARCHAR };
        getJdbcTemplate().update(DELETE_SQL, params, types);
    }

    public void update(CallInfo callInfo) {
        List<Object> paramList = createParams(callInfo);
        // replace version id with new one
        paramList.set(V_ID_POS, callInfo.generateNewVersionId());
        // remove ID
        paramList.remove(0);
        // add the ID at the end of the list
        paramList.add(callInfo.getId());
        // add old version ID
        String oldVersionId = callInfo.getVersionId();
        paramList.add(oldVersionId);

        Object[] params = paramList.toArray();

        int rowsUpdated = getJdbcTemplate().update(UPDATE_SQL, params, UPDATE_TYPES);
        if (1 != rowsUpdated)
            throw new ConcurrentUpdateException(callInfo.getId(),
                    String.format("Info %s modified in database, try again", callInfo.getId()));
    }

    public int size() {
        return getJdbcTemplate().queryForInt(COUNT_SQL);
    }

    public CallInfo findCallForDialogId(String dialogId) {
        log.debug(String.format("findCallForDialogId(%s)", dialogId));
        return findCallForDialogId(SELECT_WHERE_DIALOG_ID_SQL,
                new Object[] { dialogId, dialogId, dialogId, dialogId }, dialogId);
    }

    public CallInfo findCallForDialogId(String dialogId, String ignoreCallId) {
        log.debug(String.format("findCallForDialogId(%s, %s)", dialogId, ignoreCallId));
        return findCallForDialogId(SELECT_WHERE_DIALOG_ID_IGNORE_CALL_ID_SQL,
                new Object[] { ignoreCallId, dialogId, dialogId, ignoreCallId, dialogId, dialogId }, dialogId);
    }

    @SuppressWarnings("unchecked")
    private CallInfo findCallForDialogId(String sql, Object[] params, String dialogId) {
        if (null == dialogId)
            return null;
        List result = getJdbcTemplate().query(sql, params, new CallInfoRowMapper());
        if (result.size() < 1) {
            log.debug(String.format(NO_CALL_FOUND_FOR_DIALOG_S_RETURNING_NULL, dialogId));
            return null;
        }
        return (CallInfo) result.get(0);
    }

    public long countByCallState(CallState state, Set<String> callIds) {
        if (state == null)
            return 0;
        if (callIds == null) {
            long count = getJdbcTemplate().queryForLong(COUNT_SQL_BY_STATE, new Object[] { state.toString() });
            return count;
        }
        if (callIds.size() == 0)
            return 0;
        StringBuffer buffer = new StringBuffer();
        String[] params = new String[callIds.size() + 1];
        params[0] = state.toString();
        buffer.append(" and callId in (");
        String[] callIdsArray = callIds.toArray(new String[callIds.size()]);
        for (int i = 1; i < callIds.size(); i++) {
            buffer.append("?,");
            params[i] = callIdsArray[i - 1];
        }
        params[params.length - 1] = callIdsArray[callIdsArray.length - 1];
        buffer.append("?)");
        long count = getJdbcTemplate().queryForLong(COUNT_SQL_BY_STATE + buffer.toString(), params);
        return count;
    }

    public ConcurrentMap<String, CallInfo> getAll() {
        return getSelectedCalls(SELECT_ALL_SQL);
    }

    public ConcurrentMap<String, CallInfo> findConnectedMaxDurationCalls() {
        return getSelectedCalls(SELECT_CONNECTED_MAX_DURATION_CALLS_SQL);
    }

    @SuppressWarnings("unchecked")
    private ConcurrentMap<String, CallInfo> getSelectedCalls(String sql) {
        ConcurrentMap<String, CallInfo> result = new ConcurrentHashMap<String, CallInfo>();
        List records = getJdbcTemplate().query(sql, new CallInfoRowMapper());
        for (int i = 0; i < records.size(); i++) {
            CallInfo callInfo = (CallInfo) records.get(i);
            result.put(callInfo.getId(), callInfo);
        }
        return result;
    }

    @Override
    public String getDeleteByHouseKeeperFlagsSql() {
        return CALLINFO_DELETE_BY_HOUSEKEEPER_FLAGS_SQL;
    }

    @Override
    public String getUpdateByHouseKeeperFlagsSql() {
        return CALLINFO_UPDATE_BY_HOUSEKEEPER_FLAGS_SQL;
    }

    @Override
    public String getFindByHouseKeeperFlagsSql() {
        return CALLINFO_FIND_BY_HOUSEKEEPER_FLAGS_SQL;
    }
}