All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mobicents.smsc.cassandra.DBOperations_C2 Maven / Gradle / Ivy

There is a newer version: 7.4.179
Show newest version
/*
 * TeleStax, Open Source Cloud Communications  Copyright 2012. 
 * and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This 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 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.mobicents.smsc.cassandra;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.xml.XMLObjectReader;
import javolution.xml.XMLObjectWriter;
import javolution.xml.stream.XMLStreamException;

import org.apache.log4j.Logger;
import org.mobicents.protocols.ss7.map.api.smstpdu.CharacterSet;
import org.mobicents.protocols.ss7.map.api.smstpdu.DataCodingScheme;
import org.mobicents.protocols.ss7.map.smstpdu.DataCodingSchemeImpl;
import org.mobicents.smsc.library.DbSmsRoutingRule;
import org.mobicents.smsc.library.SmType;
import org.mobicents.smsc.library.Sms;
import org.mobicents.smsc.library.SmsSet;
import org.mobicents.smsc.library.SmsSetCache;
import org.mobicents.smsc.library.TargetAddress;
import org.mobicents.smsc.smpp.TlvSet;

import com.cloudhopper.smpp.SmppConstants;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Cluster.Builder;
import com.datastax.driver.core.Host;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.Metadata;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ProtocolVersion;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.TableMetadata;
import com.datastax.driver.core.exceptions.InvalidQueryException;

/**
 * 
 * @author sergey vetyutnev
 * 
 */
public class DBOperations_C2 {
	private static final Logger logger = Logger.getLogger(DBOperations_C2.class);

	public static final String TLV_SET = "tlvSet";
	public static final UUID emptyUuid = UUID.nameUUIDFromBytes(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			0, 0 });
	public static final int IN_SYSTEM_UNSENT = 0;
	public static final int IN_SYSTEM_INPROCESS = 1;
	public static final int IN_SYSTEM_SENT = 2;

	public static final int CURRENT_DUE_SLOT = 0;
    public static final int NEXT_MESSAGE_ID = 1;
    public static final int NEXT_CORRELATION_ID = 2;
	public static final long MAX_MESSAGE_ID = 10000000000L;
    public static final long MESSAGE_ID_LAG = 1000;
    public static final long DUE_SLOT_WRITING_POSSIBILITY_DELAY = 10;

	private static final DBOperations_C2 instance = new DBOperations_C2();

	// cassandra access
	private Cluster cluster;
	protected Session session;

	// hardcored configuring data - for table structures

	// multiTableModel: splitting database tables depending on dates
	private boolean multiTableModel = true;
	// length of the due_slot in milliseconds
	private int slotMSecondsTimeArea = 1000;
	// how many days one table carries (if value is <1 or >30 this means one
	// month)
	private int dataTableDaysTimeArea = 1;
	// the date from which due_slots are calculated (01.01.2000)
	private Date slotOrigDate = new Date(100, 0, 1);

	// configurable configuring data - for table structures

	// TTL for DST_SLOT_TABLE and SLOT_MESSAGES_TABLE tables (0 - no TTL)
	private int ttlCurrent = 0;
	// TTL for MESSAGES table (0 - no TTL)
	private int ttlArchive = 0;
	// timeout of finishing of writing on new income messages (in
	// dueSlotWritingArray) (5 sec)
	private int millisecDueSlotWritingTimeout = 5000;
	// due_slot count for forward storing after current processing due_slot
	private int dueSlotForwardStoring;
	// due_slot count for which previous loaded records were revised
	private int dueSlotReviseOnSmscStart;
	// Timeout of life cycle of SmsSet in SmsSetCashe.ProcessingSmsSet in
	// seconds
	private int processingSmsSetTimeout;

	// data for processing

	private long currentDueSlot = 0;
	private long messageId = 0;
	private UUID currentSessionUUID;

	private FastMap dataTableRead = new FastMap();
	private FastMap dueSlotWritingArray = new FastMap();

	// prepared general statements
	private PreparedStatement selectCurrentSlotTable;
	private PreparedStatement updateCurrentSlotTable;
	private PreparedStatement getSmppSmsRoutingRule;
	private PreparedStatement getSipSmsRoutingRule;

	private PreparedStatement updateSmppSmsRoutingRule;
	private PreparedStatement updateSipSmsRoutingRule;

	private PreparedStatement deleteSmppSmsRoutingRule;
	private PreparedStatement deleteSipSmsRoutingRule;

	private PreparedStatement getSmppSmsRoutingRulesRange;
	private PreparedStatement getSmppSmsRoutingRulesRange2;

	private PreparedStatement getSipSmsRoutingRulesRange;
	private PreparedStatement getSipSmsRoutingRulesRange2;
	
//	private PreparedStatement getTableList;

	private Date pcsDate;
	private PreparedStatementCollection_C3[] savedPsc;

	private volatile boolean started = false;
	private boolean databaseAvailable = false;

	protected DBOperations_C2() {
		super();
	}

	public static DBOperations_C2 getInstance() {
		return instance;
	}

    // here are methods that contain code for cassandra access that is different between 2.* and 3.* cassandra versions
    public static void setBoundStatementDate(BoundStatement boundStatement, String columnName, Date date) {
        // drivers 3.0
        boundStatement.setTimestamp(columnName, date);
        // drivers 2.1
        // boundStatement.setDate(columnName, date);
    }

    public static Date getRowDate(Row row, String columnName) {
        // drivers 3.0
        return row.getTimestamp(columnName);
        // drivers 2.1
        // return row.getDate(columnName);
    }

    public static ProtocolVersion getProtocolVersion(Cluster cluster) {
        // drivers 3.0
        return cluster.getConfiguration().getProtocolOptions().getProtocolVersion();
        // drivers 2.1
        // return cluster.getConfiguration().getProtocolOptions().getProtocolVersionEnum();
    }

    public boolean isStarted() {
        return started;
    }

    public boolean isDatabaseAvailable() {
        return databaseAvailable;
    }

	protected Session getSession() {
		return this.session;
	}

	public void start(String hosts, int port, String keyspace, int secondsForwardStoring, int reviseSecondsOnSmscStart,
			int processingSmsSetTimeout) throws Exception {
		if (this.started) {
			throw new Exception("DBOperations already started");
		}

		if (secondsForwardStoring < 3)
			secondsForwardStoring = 3;
		this.dueSlotForwardStoring = secondsForwardStoring * 1000 / slotMSecondsTimeArea;
		this.dueSlotReviseOnSmscStart = reviseSecondsOnSmscStart * 1000 / slotMSecondsTimeArea;
        this.processingSmsSetTimeout = processingSmsSetTimeout;

		this.pcsDate = null;
		currentSessionUUID = UUID.randomUUID();

        Builder builder = Cluster.builder();

        try {
            String[] cassHostsArray = hosts.split(",");
            builder.addContactPoints(cassHostsArray);
            builder.withPort(port);

            this.cluster = builder.build().init();
        } catch (Exception e) {
            logger.error(
                    String.format(
                            "Failure of connting to cassandra database. : host=%s, port=%d. SMSC GW will work without database support\n",
                            hosts, port), e);
            this.started = true;
            return;
        }
        databaseAvailable = true;

        Metadata metadata = cluster.getMetadata();
        logger.info(String.format("Connected to cluster: %s\n", metadata.getClusterName()));
        for (Host host : metadata.getAllHosts()) {
            logger.info(String.format("Datacenter: %s; Host: %s; Rack: %s\n", host.getDatacenter(), host.getAddress(),
                    host.getRack()));
        }
		session = cluster.connect();

		session.execute("USE \"" + keyspace + "\"");

		this.checkCurrentSlotTableExists();

		String sa = "SELECT \"" + Schema.COLUMN_NEXT_SLOT + "\" FROM \"" + Schema.FAMILY_CURRENT_SLOT_TABLE
				+ "\" where \"" + Schema.COLUMN_ID + "\"=?;";
		selectCurrentSlotTable = session.prepare(sa);
		sa = "INSERT INTO \"" + Schema.FAMILY_CURRENT_SLOT_TABLE + "\" (\"" + Schema.COLUMN_ID + "\", \""
				+ Schema.COLUMN_NEXT_SLOT + "\") VALUES (?, ?);";
		updateCurrentSlotTable = session.prepare(sa);

        getSmppSmsRoutingRule = session.prepare("select * from \"" + Schema.FAMILY_SMPP_SMS_ROUTING_RULE + "\" where \"" + Schema.COLUMN_ADDRESS
                + "\"=? and \"" + Schema.COLUMN_NETWORK_ID + "\"=?;");
        getSipSmsRoutingRule = session.prepare("select * from \"" + Schema.FAMILY_SIP_SMS_ROUTING_RULE + "\" where \"" + Schema.COLUMN_ADDRESS + "\"=? and \""
                + Schema.COLUMN_NETWORK_ID + "\"=?;");

        updateSmppSmsRoutingRule = session.prepare("INSERT INTO \"" + Schema.FAMILY_SMPP_SMS_ROUTING_RULE + "\" (\"" + Schema.COLUMN_ADDRESS + "\", \""
                + Schema.COLUMN_NETWORK_ID + "\", \"" + Schema.COLUMN_CLUSTER_NAME + "\") VALUES (?, ?, ?);");
        updateSipSmsRoutingRule = session.prepare("INSERT INTO \"" + Schema.FAMILY_SIP_SMS_ROUTING_RULE + "\" (\"" + Schema.COLUMN_ADDRESS + "\", \""
                + Schema.COLUMN_NETWORK_ID + "\", \"" + Schema.COLUMN_CLUSTER_NAME + "\") VALUES (?, ?, ?);");

        deleteSmppSmsRoutingRule = session.prepare("delete from \"" + Schema.FAMILY_SMPP_SMS_ROUTING_RULE + "\" where \"" + Schema.COLUMN_ADDRESS
                + "\"=? and \"" + Schema.COLUMN_NETWORK_ID + "\"=?;");
        deleteSipSmsRoutingRule = session.prepare("delete from \"" + Schema.FAMILY_SIP_SMS_ROUTING_RULE + "\" where \"" + Schema.COLUMN_ADDRESS + "\"=? and \""
                + Schema.COLUMN_NETWORK_ID + "\"=?;");

		int row_count = 100;

		getSmppSmsRoutingRulesRange = session.prepare("select * from \"" + Schema.FAMILY_SMPP_SMS_ROUTING_RULE
				+ "\" where token(\"" + Schema.COLUMN_ADDRESS + "\") >= token(?) LIMIT " + row_count + ";");
		getSmppSmsRoutingRulesRange2 = session.prepare("select * from \"" + Schema.FAMILY_SMPP_SMS_ROUTING_RULE
				+ "\"  LIMIT " + row_count + ";");

		getSipSmsRoutingRulesRange = session.prepare("select * from \"" + Schema.FAMILY_SIP_SMS_ROUTING_RULE
				+ "\" where token(\"" + Schema.COLUMN_ADDRESS + "\") >= token(?) LIMIT " + row_count + ";");
		getSipSmsRoutingRulesRange2 = session.prepare("select * from \"" + Schema.FAMILY_SIP_SMS_ROUTING_RULE
				+ "\"  LIMIT " + row_count + ";");

//        getTableList = session.prepare("select * from system.schema_columnfamilies;");

		try {
			currentDueSlot = c2_getCurrentSlotTable(CURRENT_DUE_SLOT);
			if (currentDueSlot == 0) {
				// not yet set
				long l1 = this.c2_getDueSlotForTime(new Date());
				this.c2_setCurrentDueSlot(l1);
			} else {
				this.c2_setCurrentDueSlot(currentDueSlot - dueSlotReviseOnSmscStart);
			}

            messageId = c2_getCurrentSlotTable(NEXT_MESSAGE_ID);
            messageId += MESSAGE_ID_LAG;
            c2_setCurrentSlotTable(NEXT_MESSAGE_ID, messageId);
		} catch (Exception e1) {
			String msg = "Failed reading a currentDueSlot !";
			throw new PersistenceException(msg, e1);
		}

		this.started = true;
	}

	public void stop() throws Exception {
		if (!this.started)
			return;

        if (cluster != null && !cluster.isClosed()) {
            Metadata metadata = cluster.getMetadata();
            cluster.close();
            logger.info(String.format("Disconnected from cluster: %s\n", metadata.getClusterName()));
        }

		this.started = false;
	}

	/**
	 * Returns length of the due_slot in milliseconds
	 *
	 */
	public long getSlotMSecondsTimeArea() {
		return slotMSecondsTimeArea;
	}

	/**
	 * Returns due_slot for the given time
	 */
	public long c2_getDueSlotForTime(Date time) {
		long a2 = time.getTime();
		long a1 = this.slotOrigDate.getTime();
		long diff = a2 - a1;
		long res = diff / this.slotMSecondsTimeArea;
		return res;
	}

	/**
	 * Returns time for the given due_slot
	 */
	public Date c2_getTimeForDueSlot(long dueSlot) {
		long a1 = this.slotOrigDate.getTime() + dueSlot * this.slotMSecondsTimeArea;
		Date date = new Date(a1);
		return date;
	}

	/**
	 * Returns due_slop that SMSC is processing now
	 */
	public long c2_getCurrentDueSlot() {
		return currentDueSlot;
	}

	/**
	 * Set a new due_slop that SMSC "is processing now" and store it into the
	 * database
	 */
	public void c2_setCurrentDueSlot(long newDueSlot) throws PersistenceException {
		currentDueSlot = newDueSlot;

		c2_setCurrentSlotTable(CURRENT_DUE_SLOT, newDueSlot);
	}

	/**
	 * Returns a next messageId
	 * Every MESSAGE_ID_LAG messageId will be stored at cassandra database
	 */
	public synchronized long c2_getNextMessageId() {
		messageId++;
		if (messageId >= MAX_MESSAGE_ID)
			messageId = 1;
		if (messageId % MESSAGE_ID_LAG == 0 && databaseAvailable) {
			try {
				c2_setCurrentSlotTable(NEXT_MESSAGE_ID, messageId);
			} catch (PersistenceException e) {
				logger.error("Exception when storing next messageId to the database: " + e.getMessage(), e);
			}
		}

		return messageId;
	}

    /**
     * Initial reading CURRENT_DUE_SLOT and NEXT_MESSAGE_ID when cassandra
     * database access starting
     * 
     * @param CURRENT_DUE_SLOT
     *            or NEXT_MESSAGE_ID
     * @return dead value
     * @throws PersistenceException
     */
	protected long c2_getCurrentSlotTable(int key) throws PersistenceException {
		PreparedStatement ps = selectCurrentSlotTable;
		BoundStatement boundStatement = new BoundStatement(ps);
		boundStatement.bind(key);
		ResultSet res = session.execute(boundStatement);

		long res2 = 0;
		for (Row row : res) {
			res2 = row.getLong(0);
			break;
		}
		return res2;
	}

	private void c2_setCurrentSlotTable(int key, long newVal) throws PersistenceException {
		try {
			PreparedStatement ps = updateCurrentSlotTable;
			BoundStatement boundStatement = new BoundStatement(ps);
			boundStatement.bind(key, newVal);
			ResultSet res = session.execute(boundStatement);
		} catch (Exception e1) {
			String msg = "Failed writing a c2_setCurrenrSlotTable !";
			throw new PersistenceException(msg, e1);
		}
	}

	/**
	 * Return due_slop for current time
	 */
	public long c2_getIntimeDueSlot() {
		return this.c2_getDueSlotForTime(new Date());
	}

	/**
	 * Return due_slop for storing next incoming to SMSC message
	 */
	public long c2_getDueSlotForNewSms() {
		long res = c2_getIntimeDueSlot() + dueSlotForwardStoring;
		return res;

		// TODO: we can add here code incrementing of due_slot if current
		// due_slot is overloaded
	}

    /**
     * Checking if dueSlot is possible to use for writing (it is not too soon).
     * if possible - returns dueSlot
     * if not - returns next possible dueSlot
     */
    public long c2_checkDueSlotWritingPossibility(long dueSlot) {
        long first = this.c2_getCurrentDueSlot() + DUE_SLOT_WRITING_POSSIBILITY_DELAY;
        if (dueSlot < first)
            return first;
        else
            return dueSlot;
    }

	/**
	 * Registering that thread starts writing to this due_slot
	 */
	public void c2_registerDueSlotWriting(long dueSlot) {
		synchronized (dueSlotWritingArray) {
			Long ll = dueSlot;
			DueSlotWritingElement el = dueSlotWritingArray.get(ll);
			if (el == null) {
				el = new DueSlotWritingElement(ll);
				dueSlotWritingArray.put(ll, el);
				el.writingCount = 1;
			} else {
				el.writingCount++;
			}
			el.lastStartDate = new Date();
		}
	}

	/**
	 * Registering that thread finishes writing to this due_slot
	 */
	public void c2_unregisterDueSlotWriting(long dueSlot) {
		synchronized (dueSlotWritingArray) {
			Long ll = dueSlot;
			DueSlotWritingElement el = dueSlotWritingArray.get(ll);
			if (el != null) {
				el.writingCount--;
				if (el.writingCount == 0) {
					dueSlotWritingArray.remove(ll);
				}
			}
		}
	}

	/**
	 * Checking if due_slot is not in writing state now Returns true if due_slot
	 * is not in writing now
	 */
	public boolean c2_checkDueSlotNotWriting(long dueSlot) {
		synchronized (dueSlotWritingArray) {
			Long ll = dueSlot;
			DueSlotWritingElement el = dueSlotWritingArray.get(ll);
			if (el != null) {
				Date d1 = el.lastStartDate;
				Date d2 = new Date();
				long diff = d2.getTime() - d1.getTime();
				if (diff > millisecDueSlotWritingTimeout) {
					logger.warn("Timeout in millisecDueSlotWritingTimeout element");
					dueSlotWritingArray.remove(ll);
					return true;
				} else
					return false;
			} else {
				return true;
			}
		}
	}

	/**
	 * Generate a table name depending on long dueSlot
	 */
	protected String getTableName(long dueSlot) {
		Date dt = this.c2_getTimeForDueSlot(dueSlot);
		return getTableName(dt);
	}

	/**
	 * Generate a table name depending on date
	 */
	protected String getTableName(Date dt) {
		if (multiTableModel) {
			StringBuilder sb = new StringBuilder();
			sb.append("_");
			sb.append(dt.getYear() + 1900);
			sb.append("_");
			int mn = dt.getMonth() + 1;
			if (mn >= 10)
				sb.append(mn);
			else {
				sb.append("0");
				sb.append(mn);
			}
			if (this.dataTableDaysTimeArea < 1 || this.dataTableDaysTimeArea >= 30) {
			} else {
				int dy = dt.getDate();
				int fNum;
				if (this.dataTableDaysTimeArea == 1)
					fNum = dy / this.dataTableDaysTimeArea;
				else
					fNum = dy / this.dataTableDaysTimeArea + 1;
				sb.append("_");
				if (fNum >= 10)
					sb.append(fNum);
				else {
					sb.append("0");
					sb.append(fNum);
				}
			}
			return sb.toString();
		} else
			return "";
	}

	public PreparedStatementCollection_C3[] c2_getPscList() throws PersistenceException {
		Date dt = new Date();
		if (!this.isStarted())
			return new PreparedStatementCollection_C3[0];
		if (pcsDate != null && dt.getDate() == pcsDate.getDate()) {
			return savedPsc;
		} else {
			createPscList();
			return savedPsc;
		}
	}

	private void createPscList() throws PersistenceException {
		Date dt = new Date();
		Date dtt = new Date(dt.getTime() + 1000 * 60 * 60 * 24);
		String s1 = this.getTableName(dt);
		String s2 = this.getTableName(dtt);
		PreparedStatementCollection_C3[] res;
		if (!s2.equals(s1)) {
			res = new PreparedStatementCollection_C3[2];
			res[0] = this.getStatementCollection(dtt);
			res[1] = this.getStatementCollection(dt);
		} else {
			res = new PreparedStatementCollection_C3[1];
			res[0] = this.getStatementCollection(dt);
		}
		savedPsc = res;

		pcsDate = dt;
	}

	public long c2_getDueSlotForTargetId(String targetId) throws PersistenceException {
		PreparedStatementCollection_C3[] lstPsc = this.c2_getPscList();

		for (PreparedStatementCollection_C3 psc : lstPsc) {
			long dueSlot = this.c2_getDueSlotForTargetId(psc, targetId);
			if (dueSlot != 0)
				return dueSlot;
		}
		return 0;
	}

	public long c2_getDueSlotForTargetId(PreparedStatementCollection_C3 psc, String targetId)
			throws PersistenceException {
		try {
			PreparedStatement ps = psc.getDueSlotForTargetId;
			BoundStatement boundStatement = new BoundStatement(ps);
			boundStatement.bind(targetId);
			ResultSet res = session.execute(boundStatement);

			long l = 0;
			for (Row row : res) {
				l = row.getLong(0);
				break;
			}
			return l;
		} catch (Exception e1) {
			// TODO: remove it
			e1.printStackTrace();

			String msg = "Failed to execute getDueSlotForTargetId() !";
			throw new PersistenceException(msg, e1);
		}
	}

	public void c2_updateDueSlotForTargetId(String targetId, long newDueSlot) throws PersistenceException {
		PreparedStatementCollection_C3 psc = this.getStatementCollection(newDueSlot);

		try {
			PreparedStatement ps = psc.createDueSlotForTargetId;
			BoundStatement boundStatement = new BoundStatement(ps);
			boundStatement.bind(targetId, newDueSlot);
			ResultSet res = session.execute(boundStatement);
		} catch (Exception e1) {
			String msg = "Failed to execute createDueSlotForTargetId() !";
			throw new PersistenceException(msg, e1);
		}
	}

	private void c2_clearDueSlotForTargetId(String targetId, long newDueSlot) throws PersistenceException {
		PreparedStatementCollection_C3 psc = this.getStatementCollection(newDueSlot);

		try {
			PreparedStatement ps = psc.createDueSlotForTargetId;
			BoundStatement boundStatement = new BoundStatement(ps);
			boundStatement.bind(targetId, 0L);
			ResultSet res = session.execute(boundStatement);
		} catch (Exception e1) {
			String msg = "Failed to execute clearDueSlotForTargetId() !";
			throw new PersistenceException(msg, e1);
		}
	}

	public void c2_updateDueSlotForTargetId_WithTableCleaning(String targetId, long newDueSlot)
			throws PersistenceException {
		// removing dueSlot for other time tables is any
		PreparedStatementCollection_C3[] lstPsc = this.c2_getPscList();
		if (lstPsc.length >= 2) {
			String s1 = this.getTableName(newDueSlot);
			for (int i1 = 0; i1 < lstPsc.length; i1++) {
				PreparedStatementCollection_C3 psc = lstPsc[i1];
				if (!psc.getTName().equals(s1)) {
					long dueSlot = this.c2_getDueSlotForTargetId(psc, targetId);
					if (dueSlot != 0) {
						c2_clearDueSlotForTargetId(targetId, dueSlot);
					}
				}
			}
		}

		c2_updateDueSlotForTargetId(targetId, newDueSlot);
	}

    /**
     * scheduler a message into a dueSlot that is already scheduled for all
     * messages for this targetId or for a next available if no dueSlot has been
     * scheduled for targetId
     */
    public void c2_scheduleMessage_ReschedDueSlot(Sms sms, boolean fastStoreAndForwordMode, boolean removeExpiredValidityPeriod) throws PersistenceException {
        c2_scheduleMessage_ReschedDueSlot(sms, fastStoreAndForwordMode, 0, null, removeExpiredValidityPeriod);
    }

    private void c2_scheduleMessage_ReschedDueSlot(Sms sms, boolean fastStoreAndForwordMode, long preferredDueSlot, ArrayList lstFailured,
            boolean removeExpiredValidityPeriod) throws PersistenceException {
        if (sms.getStored()) {
            long dueSlot = 0;
            PreparedStatementCollection_C3[] lstPsc = this.c2_getPscList();
            boolean done = false;
            int cnt = 0;
            while (!done && cnt < 5) {
                cnt++;

                SmType destType = sms.getSmsSet().getType();
                if (destType == null || destType == SmType.SMS_FOR_SS7) {
                    for (PreparedStatementCollection_C3 psc : lstPsc) {
                        dueSlot = this.c2_getDueSlotForTargetId(psc, sms.getSmsSet().getTargetId());
                        if (dueSlot != 0)
                            break;
                    }

                    if ((dueSlot == 0 || dueSlot <= this.c2_getCurrentDueSlot()) && preferredDueSlot > 0) {
                        dueSlot = preferredDueSlot;
                        this.c2_updateDueSlotForTargetId_WithTableCleaning(sms.getSmsSet().getTargetId(), dueSlot);
                    }

                    if (fastStoreAndForwordMode && dueSlot != 0) {
                        long dueSlot2 = c2_checkDueSlotWritingPossibility(dueSlot);
                        if (dueSlot2 != dueSlot) {
                            dueSlot = dueSlot2;
                            this.c2_updateDueSlotForTargetId_WithTableCleaning(sms.getSmsSet().getTargetId(), dueSlot);
                        }
                    }
                }

                if (dueSlot == 0 || dueSlot <= this.c2_getCurrentDueSlot()) {
                    dueSlot = this.c2_getDueSlotForNewSms();
                    this.c2_updateDueSlotForTargetId_WithTableCleaning(sms.getSmsSet().getTargetId(), dueSlot);
                }

                sms.setDueSlot(dueSlot);

                done = this.do_scheduleMessage(sms, dueSlot, lstFailured, fastStoreAndForwordMode, removeExpiredValidityPeriod);
            }

            if (!done) {
                logger.warn("5 retries of c2_scheduleMessage fails for targetId=" + sms.getSmsSet().getTargetId());
            }
        }
	}

    /**
     * scheduler a message for a predefined dueSlot (for a next schedule time)
     */
    public void c2_scheduleMessage_NewDueSlot(Sms sms, long dueSlot, ArrayList lstFailured, boolean fastStoreAndForwordMode) throws PersistenceException {
        if (sms.getStoringAfterFailure()) {
            // the first store in ForwardAndStore mode - we store with other
            // messages to this
            sms.setStored(true);
            c2_scheduleMessage_ReschedDueSlot(sms, fastStoreAndForwordMode, dueSlot, lstFailured, true);
        } else {
            if (sms.getStored()) {
                if (fastStoreAndForwordMode) {
                    dueSlot = c2_checkDueSlotWritingPossibility(dueSlot);
                }

                this.c2_updateInSystem(sms, DBOperations_C2.IN_SYSTEM_SENT, fastStoreAndForwordMode);
                this.c2_updateDueSlotForTargetId_WithTableCleaning(sms.getSmsSet().getTargetId(), dueSlot);
                this.do_scheduleMessage(sms, dueSlot, lstFailured, fastStoreAndForwordMode, true);
            } else {
                lstFailured.add(sms);
            }
        }
    }

	protected boolean do_scheduleMessage(Sms sms, long dueSlot, ArrayList lstFailured, boolean fastStoreAndForwordMode, boolean removeExpiredValidityPeriod)
            throws PersistenceException {

		sms.setDueSlot(dueSlot);

		Date dt = this.c2_getTimeForDueSlot(dueSlot);

		// special case for ScheduleDeliveryTime
		Date schedTime = sms.getScheduleDeliveryTime();
		if (schedTime != null && schedTime.after(dt)) {
			dueSlot = this.c2_getDueSlotForTime(schedTime);
			sms.setDueSlot(dueSlot);
		}

		// checking validity date
		if (sms.getValidityPeriod() != null && sms.getValidityPeriod().before(dt)) {
			if (lstFailured != null)
				lstFailured.add(sms);
			return true;
		}

        if (fastStoreAndForwordMode) {
            return doCreateRecordCurrent(sms, dueSlot);
        } else {
            this.c2_registerDueSlotWriting(dueSlot);
            try {
                return doCreateRecordCurrent(sms, dueSlot);
            } finally {
                this.c2_unregisterDueSlotWriting(dueSlot);
            }
        }
	}

    private boolean doCreateRecordCurrent(Sms sms, long dueSlot) throws PersistenceException {
        if (dueSlot <= this.c2_getCurrentDueSlot()) {
        	return false;
        } else {
        	this.c2_createRecordCurrent(sms);
        	return true;
        }
    }

	public void c2_createRecordCurrent(Sms sms) throws PersistenceException {
		long dueSlot = sms.getDueSlot();
		PreparedStatementCollection_C3 psc = getStatementCollection(dueSlot);

		try {
			PreparedStatement ps = psc.createRecordCurrent;
			BoundStatement boundStatement = new BoundStatement(ps);

            setSmsFields(sms, dueSlot, boundStatement, false, psc.getShortMessageNewStringFormat(), psc.getAddedCorrId(), psc.getAddedNetworkId(),
                    psc.getAddedOrigNetworkId());

			ResultSet res = session.execute(boundStatement);
		} catch (Exception e1) {
			String msg = "Failed createRecordCurrent !" + e1.getMessage();

			throw new PersistenceException(msg, e1);
		}
	}

	public void c2_createRecordArchive(Sms sms) throws PersistenceException {
        if (!this.databaseAvailable)
            return;

		Date deliveryDate = sms.getDeliverDate();
		if (deliveryDate == null)
			deliveryDate = new Date();
		long dueSlot = this.c2_getDueSlotForTime(deliveryDate);
		PreparedStatementCollection_C3 psc = getStatementCollection(deliveryDate);

		try {
			PreparedStatement ps = psc.createRecordArchive;
			BoundStatement boundStatement = new BoundStatement(ps);

            setSmsFields(sms, dueSlot, boundStatement, true, psc.getShortMessageNewStringFormat(), psc.getAddedCorrId(), psc.getAddedNetworkId(),
                    psc.getAddedOrigNetworkId());

			ResultSet res = session.execute(boundStatement);
		} catch (Exception e1) {
			String msg = "Failed createRecordArchive !" + e1.getMessage();

			throw new PersistenceException(msg, e1);
		}
	}

    private void setSmsFields(Sms sms, long dueSlot, BoundStatement boundStatement, boolean archive, boolean shortMessageNewStringFormat, boolean addedCorrId,
            boolean addedNetworkId, boolean addedOrigNetworkId) throws PersistenceException {
		boundStatement.setUUID(Schema.COLUMN_ID, sms.getDbId());
        boundStatement.setString(Schema.COLUMN_TARGET_ID, sms.getSmsSet().getTargetId());
        if (addedNetworkId) {
            boundStatement.setInt(Schema.COLUMN_NETWORK_ID, sms.getSmsSet().getNetworkId());
        }
		boundStatement.setLong(Schema.COLUMN_DUE_SLOT, dueSlot);
		boundStatement.setInt(Schema.COLUMN_IN_SYSTEM, IN_SYSTEM_UNSENT);
		boundStatement.setUUID(Schema.COLUMN_SMSC_UUID, emptyUuid);

		boundStatement.setString(Schema.COLUMN_ADDR_DST_DIGITS, sms.getSmsSet().getDestAddr());
		boundStatement.setInt(Schema.COLUMN_ADDR_DST_TON, sms.getSmsSet().getDestAddrTon());
		boundStatement.setInt(Schema.COLUMN_ADDR_DST_NPI, sms.getSmsSet().getDestAddrNpi());


		if (sms.getSourceAddr() != null) {
            boundStatement.setString(Schema.COLUMN_ADDR_SRC_DIGITS, sms.getSourceAddr());
        } else {
            boundStatement.setToNull(Schema.COLUMN_ADDR_SRC_DIGITS);
        }
		boundStatement.setInt(Schema.COLUMN_ADDR_SRC_TON, sms.getSourceAddrTon());
		boundStatement.setInt(Schema.COLUMN_ADDR_SRC_NPI, sms.getSourceAddrNpi());
        if (addedOrigNetworkId) {
            boundStatement.setInt(Schema.COLUMN_ORIG_NETWORK_ID, sms.getOrigNetworkId());
        }

		boundStatement.setInt(Schema.COLUMN_DUE_DELAY, sms.getSmsSet().getDueDelay());
        if (sms.getSmsSet().getStatus() != null)
            boundStatement.setInt(Schema.COLUMN_SM_STATUS, sms.getSmsSet().getStatus().getCode());
        else
            boundStatement.setToNull(Schema.COLUMN_SM_STATUS);
		boundStatement.setBool(Schema.COLUMN_ALERTING_SUPPORTED, sms.getSmsSet().isAlertingSupported());

		boundStatement.setLong(Schema.COLUMN_MESSAGE_ID, sms.getMessageId());
		boundStatement.setInt(Schema.COLUMN_MO_MESSAGE_REF, sms.getMoMessageRef());
        if (sms.getOrigEsmeName() != null) {
            boundStatement.setString(Schema.COLUMN_ORIG_ESME_NAME, sms.getOrigEsmeName());
        } else
            boundStatement.setToNull(Schema.COLUMN_ORIG_ESME_NAME);
		if (sms.getOrigSystemId() != null) {
			boundStatement.setString(Schema.COLUMN_ORIG_SYSTEM_ID, sms.getOrigSystemId());
        } else
            boundStatement.setToNull(Schema.COLUMN_ORIG_SYSTEM_ID);
		if (sms.getSubmitDate() != null) {
		    setBoundStatementDate(boundStatement, Schema.COLUMN_SUBMIT_DATE, sms.getSubmitDate());
        } else
            boundStatement.setToNull(Schema.COLUMN_SUBMIT_DATE);
		if (sms.getDeliverDate() != null) {
		    setBoundStatementDate(boundStatement, Schema.COLUMN_DELIVERY_DATE, sms.getDeliverDate());
        } else
            boundStatement.setToNull(Schema.COLUMN_DELIVERY_DATE);
		if (sms.getServiceType() != null) {
			boundStatement.setString(Schema.COLUMN_SERVICE_TYPE, sms.getServiceType());
        } else
            boundStatement.setToNull(Schema.COLUMN_SERVICE_TYPE);
		boundStatement.setInt(Schema.COLUMN_ESM_CLASS, sms.getEsmClass());
		boundStatement.setInt(Schema.COLUMN_PROTOCOL_ID, sms.getProtocolId());
		boundStatement.setInt(Schema.COLUMN_PRIORITY, sms.getPriority());

		boundStatement.setInt(Schema.COLUMN_REGISTERED_DELIVERY, sms.getRegisteredDelivery());
		boundStatement.setInt(Schema.COLUMN_REPLACE, sms.getReplaceIfPresent());
		boundStatement.setInt(Schema.COLUMN_DATA_CODING, sms.getDataCodingForDatabase());
		boundStatement.setInt(Schema.COLUMN_DEFAULT_MSG_ID, sms.getDefaultMsgId());

        if (shortMessageNewStringFormat) {
            if (sms.getShortMessageText() != null) {
                boundStatement.setString(Schema.COLUMN_MESSAGE_TEXT, sms.getShortMessageText());
            } else
                boundStatement.setToNull(Schema.COLUMN_MESSAGE_TEXT);
            if (sms.getShortMessageBin() != null) {
                boundStatement.setBytes(Schema.COLUMN_MESSAGE_BIN, ByteBuffer.wrap(sms.getShortMessageBin()));
            } else
                boundStatement.setToNull(Schema.COLUMN_MESSAGE_BIN);
            boundStatement.setToNull(Schema.COLUMN_MESSAGE);
        } else {
            // convert to an old format
            String msg = sms.getShortMessageText();
            byte[] udhData = sms.getShortMessageBin();
            byte[] textPart = null;
            DataCodingScheme dcs = new DataCodingSchemeImpl(sms.getDataCoding());
            switch (dcs.getCharacterSet()) {
            case GSM7:
                textPart = msg.getBytes();
                break;
            case UCS2:
                Charset ucs2Charset = Charset.forName("UTF-16BE");
                ByteBuffer bb = ucs2Charset.encode(msg);
                textPart = new byte[bb.limit()];
                bb.get(textPart);
                break;
            default:
                // we do not support this yet
                break;
            }

            byte[] data;
            if (textPart != null) {
                if (udhData != null) {
                    data = new byte[textPart.length + udhData.length];
                    System.arraycopy(udhData, 0, data, 0, udhData.length);
                    System.arraycopy(textPart, 0, data, udhData.length, textPart.length);
                } else {
                    data = textPart;
                }
            } else {
                if (udhData != null) {
                    data = udhData;
                } else {
                    data = new byte[0];
                }
            }
            boundStatement.setBytes(Schema.COLUMN_MESSAGE, ByteBuffer.wrap(data));
        }

		if (sms.getScheduleDeliveryTime() != null) {
		    setBoundStatementDate(boundStatement, Schema.COLUMN_SCHEDULE_DELIVERY_TIME, sms.getScheduleDeliveryTime());
        } else
            boundStatement.setToNull(Schema.COLUMN_SCHEDULE_DELIVERY_TIME);
		if (sms.getValidityPeriod() != null) {
		    setBoundStatementDate(boundStatement, Schema.COLUMN_VALIDITY_PERIOD, sms.getValidityPeriod());
        } else
            boundStatement.setToNull(Schema.COLUMN_VALIDITY_PERIOD);

		boundStatement.setInt(Schema.COLUMN_DELIVERY_COUNT, sms.getDeliveryCount());

		if (sms.getTlvSet().getOptionalParameterCount() > 0) {
			try {
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				XMLObjectWriter writer = XMLObjectWriter.newInstance(baos);
				writer.setIndentation("\t");
				writer.write(sms.getTlvSet(), TLV_SET, TlvSet.class);
				writer.close();
				byte[] rawData = baos.toByteArray();
				String serializedEvent = new String(rawData);

				boundStatement.setString(Schema.COLUMN_OPTIONAL_PARAMETERS, serializedEvent);
			} catch (XMLStreamException e) {
				String msg = "XMLStreamException when serializing optional parameters for '" + sms.getDbId() + "'!";

				throw new PersistenceException(msg, e);
			}
		} else {
            boundStatement.setToNull(Schema.COLUMN_OPTIONAL_PARAMETERS);
		}

        if (!archive) {
            if (addedCorrId) {
                boundStatement.setToNull(Schema.COLUMN_CORR_ID);
            }
            boundStatement.setToNull(Schema.COLUMN_IMSI);
            if (sms.getSmsSet().getCorrelationId() != null) {
                if (addedCorrId) {
                    boundStatement.setString(Schema.COLUMN_CORR_ID, sms.getSmsSet().getCorrelationId());
                } else {
                    boundStatement.setString(Schema.COLUMN_IMSI, sms.getSmsSet().getCorrelationId());
                }
            }
        } else {
            if (sms.getSmsSet().getLocationInfoWithLMSI() != null
                    && sms.getSmsSet().getLocationInfoWithLMSI().getNetworkNodeNumber() != null) {
                boundStatement.setString(Schema.COLUMN_NNN_DIGITS, sms.getSmsSet().getLocationInfoWithLMSI()
                        .getNetworkNodeNumber().getAddress());
                boundStatement.setInt(Schema.COLUMN_NNN_AN, sms.getSmsSet().getLocationInfoWithLMSI().getNetworkNodeNumber()
                        .getAddressNature().getIndicator());
                boundStatement.setInt(Schema.COLUMN_NNN_NP, sms.getSmsSet().getLocationInfoWithLMSI().getNetworkNodeNumber()
                        .getNumberingPlan().getIndicator());
            } else {
                boundStatement.setToNull(Schema.COLUMN_NNN_DIGITS);
                boundStatement.setToNull(Schema.COLUMN_NNN_AN);
                boundStatement.setToNull(Schema.COLUMN_NNN_NP);
            }
            if (sms.getSmsSet().getType() != null) {
                boundStatement.setInt(Schema.COLUMN_SM_TYPE, sms.getSmsSet().getType().getCode());
            } else {
                boundStatement.setToNull(Schema.COLUMN_SM_TYPE);
            }
            if (addedCorrId) {
                boundStatement.setString(Schema.COLUMN_CORR_ID, sms.getSmsSet().getCorrelationId());
            }
            boundStatement.setString(Schema.COLUMN_IMSI, sms.getSmsSet().getImsi());
        }
	}

	public ArrayList c2_getRecordList(long dueSlot) throws PersistenceException {
		PreparedStatementCollection_C3 psc = getStatementCollection(dueSlot);

		ArrayList result = new ArrayList();
		try {
			PreparedStatement ps = psc.getRecordData;
			BoundStatement boundStatement = new BoundStatement(ps);
			boundStatement.bind(dueSlot);
			ResultSet res = session.execute(boundStatement);

			for (Row row : res) {
                SmsSet smsSet = this.createSms(row, null, psc.getShortMessageNewStringFormat(), psc.getAddedCorrId(), psc.getAddedNetworkId(),
                        psc.getAddedOrigNetworkId());
				if (smsSet != null)
					result.add(smsSet);
			}
		} catch (Exception e1) {
			String msg = "Failed getRecordList()";

			throw new PersistenceException(msg, e1);
		}

		return result;
	}

	public SmsSet c2_getRecordListForTargeId(long dueSlot, String targetId) throws PersistenceException {
		PreparedStatementCollection_C3 psc = getStatementCollection(dueSlot);

		SmsSet result = null;
		try {
			PreparedStatement ps = psc.getRecordData2;
			BoundStatement boundStatement = new BoundStatement(ps);
			boundStatement.bind(dueSlot, targetId);
			ResultSet res = session.execute(boundStatement);

			for (Row row : res) {
                result = this.createSms(row, result, psc.getShortMessageNewStringFormat(), psc.getAddedCorrId(), psc.getAddedNetworkId(),
                        psc.getAddedOrigNetworkId());
			}
		} catch (Exception e1) {
			String msg = "Failed getRecordListForTargeId()";

			throw new PersistenceException(msg, e1);
		}

        return result;
	}

    protected SmsSet createSms(final Row row, SmsSet smsSet, boolean shortMessageNewStringFormat, boolean addedCorrId, boolean addedNetworkId,
            boolean addedOrigNetworkId) throws PersistenceException {
        if (row == null) {
            return smsSet;
        }

		int inSystem = row.getInt(Schema.COLUMN_IN_SYSTEM);
		UUID smscUuid = row.getUUID(Schema.COLUMN_SMSC_UUID);
		if (inSystem == IN_SYSTEM_SENT || inSystem == IN_SYSTEM_INPROCESS && smscUuid.equals(currentSessionUUID)) {
		    // inSystem it is in processing or processed - skip this
			return smsSet;
		}

		Sms sms = new Sms();
		sms.setStored(true);
		sms.setDbId(row.getUUID(Schema.COLUMN_ID));
		sms.setDueSlot(row.getLong(Schema.COLUMN_DUE_SLOT));

		sms.setSourceAddr(row.getString(Schema.COLUMN_ADDR_SRC_DIGITS));
		sms.setSourceAddrTon(row.getInt(Schema.COLUMN_ADDR_SRC_TON));
		sms.setSourceAddrNpi(row.getInt(Schema.COLUMN_ADDR_SRC_NPI));
        if (addedOrigNetworkId) {
            sms.setOrigNetworkId(row.getInt(Schema.COLUMN_ORIG_NETWORK_ID));
        }

		sms.setMessageId(row.getLong(Schema.COLUMN_MESSAGE_ID));
		sms.setMoMessageRef(row.getInt(Schema.COLUMN_MO_MESSAGE_REF));
		sms.setOrigEsmeName(row.getString(Schema.COLUMN_ORIG_ESME_NAME));
		sms.setOrigSystemId(row.getString(Schema.COLUMN_ORIG_SYSTEM_ID));
        sms.setSubmitDate(getRowDate(row, Schema.COLUMN_SUBMIT_DATE));

		sms.setServiceType(row.getString(Schema.COLUMN_SERVICE_TYPE));
		sms.setEsmClass(row.getInt(Schema.COLUMN_ESM_CLASS));
		sms.setProtocolId(row.getInt(Schema.COLUMN_PROTOCOL_ID));
		sms.setPriority(row.getInt(Schema.COLUMN_PRIORITY));
		sms.setRegisteredDelivery(row.getInt(Schema.COLUMN_REGISTERED_DELIVERY));
		sms.setReplaceIfPresent(row.getInt(Schema.COLUMN_REPLACE));
		sms.setDataCodingForDatabase(row.getInt(Schema.COLUMN_DATA_CODING));
		sms.setDefaultMsgId(row.getInt(Schema.COLUMN_DEFAULT_MSG_ID));

        if (shortMessageNewStringFormat) {
            sms.setShortMessageText(row.getString(Schema.COLUMN_MESSAGE_TEXT));
            ByteBuffer bb = row.getBytes(Schema.COLUMN_MESSAGE_BIN);
            if (bb != null) {
                byte[] buf = new byte[bb.limit() - bb.position()];
                bb.get(buf);
                sms.setShortMessageBin(buf);
            }
        } else {
            ByteBuffer bb = row.getBytes(Schema.COLUMN_MESSAGE);
            if (bb != null) {
                byte[] buf = new byte[bb.limit() - bb.position()];
                bb.get(buf);
                sms.setShortMessage(buf);

                // convert to a new format
                byte[] shortMessage = sms.getShortMessage();
                byte[] textPart = shortMessage;
                byte[] udhData = null;
                boolean udhExists = false;
                DataCodingScheme dcs = new DataCodingSchemeImpl(sms.getDataCoding());
                String msg = null;
                if (dcs.getCharacterSet() == CharacterSet.GSM8) {
                    udhData = shortMessage;
                } else {
                    if ((sms.getEsmClass() & SmppConstants.ESM_CLASS_UDHI_MASK) != 0) {
                        udhExists = true;
                    }
                    if (udhExists && shortMessage != null && shortMessage.length > 2) {
                        // UDH exists
                        int udhLen = (shortMessage[0] & 0xFF) + 1;
                        if (udhLen <= shortMessage.length) {
                            textPart = new byte[shortMessage.length - udhLen];
                            udhData = new byte[udhLen];
                            System.arraycopy(shortMessage, udhLen, textPart, 0, textPart.length);
                            System.arraycopy(shortMessage, 0, udhData, 0, udhLen);
                        }
                    }

                    switch (dcs.getCharacterSet()) {
                    case GSM7:
                        msg = new String(textPart);
                        break;
                    case UCS2:
                        Charset ucs2Charset = Charset.forName("UTF-16BE");
                        bb = ByteBuffer.wrap(textPart);
                        CharBuffer bf = ucs2Charset.decode(bb);
                        msg = bf.toString();
                        break;
                    default:
                        udhData = sms.getShortMessage();
                        break;
                    }
                }

                sms.setShortMessageText(msg);
                sms.setShortMessageBin(udhData);
            }
        }

        sms.setScheduleDeliveryTime(getRowDate(row, Schema.COLUMN_SCHEDULE_DELIVERY_TIME));
		sms.setValidityPeriod(getRowDate(row, Schema.COLUMN_VALIDITY_PERIOD));
		sms.setDeliveryCount(row.getInt(Schema.COLUMN_DELIVERY_COUNT));

		String s = row.getString(Schema.COLUMN_OPTIONAL_PARAMETERS);
		if (s != null) {
			try {
				ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
				XMLObjectReader reader = XMLObjectReader.newInstance(bais);
				TlvSet copy = reader.read(TLV_SET, TlvSet.class);
				sms.getTlvSet().clearAllOptionalParameter();
				sms.getTlvSet().addAllOptionalParameter(copy.getOptionalParameters());
			} catch (XMLStreamException e) {
				String msg = "XMLStreamException when deserializing optional parameters for '" + sms.getDbId() + "'!";

				throw new PersistenceException(msg, e);
			}
		}

		if (smsSet == null) {
			smsSet = new SmsSet();

			String destAddr = null;
			int destAddrTon = -1;
			int destAddrNpi = -1;

			destAddr = row.getString(Schema.COLUMN_ADDR_DST_DIGITS);
			destAddrTon = row.getInt(Schema.COLUMN_ADDR_DST_TON);
			destAddrNpi = row.getInt(Schema.COLUMN_ADDR_DST_NPI);

			if (destAddr == null || destAddrTon == -1 || destAddrNpi == -1) {
				throw new PersistenceException("destAddr or destAddrTon or destAddrNpi is absent for ID='"
						+ sms.getDbId() + "'");
			}
			smsSet.setDestAddr(destAddr);
			smsSet.setDestAddrTon(destAddrTon);
            smsSet.setDestAddrNpi(destAddrNpi);

            if (addedNetworkId) {
                smsSet.setNetworkId(row.getInt(Schema.COLUMN_NETWORK_ID));
            } else {
                String tagId = row.getString(Schema.COLUMN_TARGET_ID);
                if (tagId != null) {
                    String[] ss = tagId.split("_");
                    if (ss.length == 4) {
                        String s1 = ss[3];
                        try {
                            int networkId = Integer.parseInt(s1);
                            smsSet.setNetworkId(networkId);
                        } catch (Exception e) {
                        }
                    }
                }
            }

            if (addedCorrId)
                smsSet.setCorrelationId(row.getString(Schema.COLUMN_CORR_ID));
            else
                smsSet.setCorrelationId(row.getString(Schema.COLUMN_IMSI));
		}
		int dueDelay = row.getInt(Schema.COLUMN_DUE_DELAY);
		if (dueDelay > smsSet.getDueDelay())
			smsSet.setDueDelay(dueDelay);

		boolean alertingSupported = row.getBool(Schema.COLUMN_ALERTING_SUPPORTED);
		if (alertingSupported)
			smsSet.setAlertingSupported(true);

		smsSet.addSms(sms);

		return smsSet;
	}

	public ArrayList c2_sortRecordList(ArrayList sourceLst) {
		FastMap res = new FastMap();

		// aggregating messages for one targetId
		for (SmsSet smsSet : sourceLst) {
			SmsSet smsSet2 = null;
			try {
				smsSet2 = res.get(smsSet.getTargetId());
			} catch (Throwable e) {
				int dd = 0;
			}
			if (smsSet2 != null) {
                smsSet2.addSms(smsSet.getSms(0));
                if (smsSet2.getCorrelationId() == null) {
                    // filling correcationId if not all SmsSet are filled
                    smsSet2.setCorrelationId(smsSet.getCorrelationId());
                }
			} else {
				res.put(smsSet.getTargetId(), smsSet);
			}
		}

		// adding into SmsSetCashe
		ArrayList res2 = new ArrayList();
		// 60 min timeout
		Date timeOutDate = new Date(new Date().getTime() - 1000 * 60 * 30);
		for (SmsSet smsSet : res.values()) {
			smsSet.resortSms();

			TargetAddress lock = SmsSetCache.getInstance().addSmsSet(new TargetAddress(smsSet));
			try {
				SmsSet smsSet2;
				synchronized (lock) {
					smsSet2 = SmsSetCache.getInstance().getProcessingSmsSet(smsSet.getTargetId());
					if (smsSet2 != null) {
						if (smsSet2.getLastUpdateTime().after(timeOutDate)) {
                            smsSet2.addSmsSet(smsSet);
//							for (int i1 = 0; i1 < smsSet.getSmsCount(); i1++) {
//                                Sms smsx = smsSet.getSms(i1);
//                                if (!smsSet2.checkSmsPresent(smsx)) {
//                                    smsSet2.addSmsSet(smsx);
//                                }
//							}
						} else {
							logger.warn("Timeout of SmsSet in ProcessingSmsSet: targetId=" + smsSet2.getTargetId()
									+ ", messageCount=" + smsSet2.getSmsCount());
							smsSet2 = smsSet;
							SmsSetCache.getInstance().addProcessingSmsSet(smsSet2.getTargetId(), smsSet2,
									processingSmsSetTimeout);
						}
					} else {
						smsSet2 = smsSet;
						SmsSetCache.getInstance().addProcessingSmsSet(smsSet2.getTargetId(), smsSet2,
								processingSmsSetTimeout);
					}
				}
				res2.add(smsSet2);
			} finally {
				SmsSetCache.getInstance().removeSmsSet(lock);
			}
		}

		return res2;
	}

    public boolean c2_checkProcessingSmsSet(SmsSet smsSet) {
        Date timeOutDate = new Date(new Date().getTime() - 1000 * 60 * 30);

        TargetAddress lock = SmsSetCache.getInstance().addSmsSet(new TargetAddress(smsSet));
        try {
            synchronized (lock) {
                SmsSet smsSet2 = SmsSetCache.getInstance().getProcessingSmsSet(smsSet.getTargetId());
                if (smsSet2 != null) {
                    if (smsSet2.getLastUpdateTime().after(timeOutDate)) {
                        smsSet2.addSmsSet(smsSet);
//                        for (int i1 = 0; i1 < smsSet.getSmsCount(); i1++) {
//                            Sms smsx = smsSet.getSms(i1);
//                            if (!smsSet2.checkSmsPresent(smsx)) {
//                                smsSet2.addSmsSet(smsx);
//                            }
//                        }
                        return false;
                    } else {
                        logger.warn("Timeout of SmsSet in ProcessingSmsSet: targetId=" + smsSet2.getTargetId() + ", messageCount=" + smsSet2.getSmsCount());
                        smsSet2 = smsSet;
                        SmsSetCache.getInstance().addProcessingSmsSet(smsSet2.getTargetId(), smsSet2, processingSmsSetTimeout);
                    }
                } else {
                    smsSet2 = smsSet;
                    SmsSetCache.getInstance().addProcessingSmsSet(smsSet2.getTargetId(), smsSet2, processingSmsSetTimeout);
                }
            }
        } finally {
            SmsSetCache.getInstance().removeSmsSet(lock);
        }

        return true;
    }

	public void c2_updateInSystem(Sms sms, int isSystemStatus, boolean fastStoreAndForwordMode) throws PersistenceException {
        // if (sms.getStored() && (!fastStoreAndForwordMode ||
        // sms.getInvokedByAlert() || )) {

        if (sms.getStored()) {
            PreparedStatementCollection_C3 psc = this.getStatementCollection(sms.getDueSlot());

            try {
                PreparedStatement ps = psc.updateInSystem;
                BoundStatement boundStatement = new BoundStatement(ps);
                boundStatement.bind(isSystemStatus, currentSessionUUID, sms.getDueSlot(), sms.getSmsSet().getTargetId(), sms.getDbId());
                ResultSet res = session.execute(boundStatement);
            } catch (Exception e1) {
                String msg = "Failed to execute updateInSystem() !";
                throw new PersistenceException(msg, e1);
            }
        }
	}

	public void c2_updateAlertingSupport(long dueSlot, String targetId, UUID dbId) throws PersistenceException {
		PreparedStatementCollection_C3 psc = this.getStatementCollection(dueSlot);

		try {
			PreparedStatement ps = psc.updateAlertingSupport;
			BoundStatement boundStatement = new BoundStatement(ps);
			boundStatement.bind(true, dueSlot, targetId, dbId);
			ResultSet res = session.execute(boundStatement);
		} catch (Exception e1) {
			String msg = "Failed to execute c2_updateAlertingSupport() !";
			throw new PersistenceException(msg, e1);
		}
	}

	public void createArchiveMessage(Sms sms) throws PersistenceException {
		// TODO: ......................
		// .....................................
	}

	protected PreparedStatementCollection_C3 getStatementCollection(Date dt) throws PersistenceException {
		String tName = this.getTableName(dt);
		PreparedStatementCollection_C3 psc = dataTableRead.get(tName);
		if (psc != null)
			return psc;

		return doGetStatementCollection(tName);
	}

	protected PreparedStatementCollection_C3 getStatementCollection(long deuSlot) throws PersistenceException {
		String tName = this.getTableName(deuSlot);
		PreparedStatementCollection_C3 psc = dataTableRead.get(tName);
		if (psc != null)
			return psc;

		return doGetStatementCollection(tName);
	}

	private synchronized PreparedStatementCollection_C3 doGetStatementCollection(String tName)
			throws PersistenceException {
		PreparedStatementCollection_C3 psc = dataTableRead.get(tName);
		if (psc != null)
			return psc;

		try {
			try {
				// checking if a datatable exists
				String s1 = "SELECT * FROM \"" + Schema.FAMILY_DST_SLOT_TABLE + tName + "\";";
				PreparedStatement ps = session.prepare(s1);
			} catch (InvalidQueryException e) {
				// datatable does not exist

				// DST_SLOT_TABLE
				StringBuilder sb = new StringBuilder();
				sb.append("CREATE TABLE \"" + Schema.FAMILY_DST_SLOT_TABLE);
				sb.append(tName);
				sb.append("\" (");

				appendField(sb, Schema.COLUMN_TARGET_ID, "ascii");
				appendField(sb, Schema.COLUMN_DUE_SLOT, "bigint");

				sb.append("PRIMARY KEY (\"");
				sb.append(Schema.COLUMN_TARGET_ID);
				sb.append("\"");
				sb.append("));");

				String s2 = sb.toString();
				PreparedStatement ps = session.prepare(s2);
				BoundStatement boundStatement = new BoundStatement(ps);
				ResultSet res = session.execute(boundStatement);

				// SLOT_MESSAGES_TABLE
				sb = new StringBuilder();
				sb.append("CREATE TABLE \"" + Schema.FAMILY_SLOT_MESSAGES_TABLE);
				sb.append(tName);
				sb.append("\" (");

				addSmsFields(sb);

				sb.append("PRIMARY KEY ((\"");
				sb.append(Schema.COLUMN_DUE_SLOT);
				sb.append("\"), \"");
				sb.append(Schema.COLUMN_TARGET_ID);
				sb.append("\", \"");
				sb.append(Schema.COLUMN_ID);
				sb.append("\"");
				sb.append("));");

				s2 = sb.toString();
				ps = session.prepare(s2);
				boundStatement = new BoundStatement(ps);
				res = session.execute(boundStatement);

				// MESSAGES
				sb = new StringBuilder();
				sb.append("CREATE TABLE \"" + Schema.FAMILY_MESSAGES);
				sb.append(tName);
				sb.append("\" (");

				addSmsFields(sb);

				sb.append("PRIMARY KEY ((\"");
				sb.append(Schema.COLUMN_ADDR_DST_DIGITS);
				sb.append("\"), \"");
				sb.append(Schema.COLUMN_ID);
				sb.append("\"");
				sb.append("));");

				s2 = sb.toString();
				ps = session.prepare(s2);
				boundStatement = new BoundStatement(ps);
				res = session.execute(boundStatement);
			}
		} catch (Exception e1) {
			String msg = "Failed to access or create table " + tName + "!";
			throw new PersistenceException(msg, e1);
		}

		psc = new PreparedStatementCollection_C3(this, tName, ttlCurrent, ttlArchive);
		dataTableRead.putEntry(tName, psc);
		return psc;
	}

	protected void addSmsFields(StringBuilder sb) {
		appendField(sb, Schema.COLUMN_ID, "uuid");
		appendField(sb, Schema.COLUMN_TARGET_ID, "ascii");
        appendField(sb, Schema.COLUMN_NETWORK_ID, "int");
		appendField(sb, Schema.COLUMN_DUE_SLOT, "bigint");
		appendField(sb, Schema.COLUMN_IN_SYSTEM, "int");
		appendField(sb, Schema.COLUMN_SMSC_UUID, "uuid");

		appendField(sb, Schema.COLUMN_ADDR_DST_DIGITS, "ascii");
		appendField(sb, Schema.COLUMN_ADDR_DST_TON, "int");
		appendField(sb, Schema.COLUMN_ADDR_DST_NPI, "int");

		appendField(sb, Schema.COLUMN_ADDR_SRC_DIGITS, "ascii");
		appendField(sb, Schema.COLUMN_ADDR_SRC_TON, "int");
        appendField(sb, Schema.COLUMN_ADDR_SRC_NPI, "int");
        appendField(sb, Schema.COLUMN_ORIG_NETWORK_ID, "int");

		appendField(sb, Schema.COLUMN_DUE_DELAY, "int");
		appendField(sb, Schema.COLUMN_ALERTING_SUPPORTED, "boolean");

		appendField(sb, Schema.COLUMN_MESSAGE_ID, "bigint");
		appendField(sb, Schema.COLUMN_MO_MESSAGE_REF, "int");
		appendField(sb, Schema.COLUMN_ORIG_ESME_NAME, "text");
		appendField(sb, Schema.COLUMN_ORIG_SYSTEM_ID, "text");
		appendField(sb, Schema.COLUMN_DEST_CLUSTER_NAME, "text");
		appendField(sb, Schema.COLUMN_DEST_ESME_NAME, "text");
		appendField(sb, Schema.COLUMN_DEST_SYSTEM_ID, "text");
		appendField(sb, Schema.COLUMN_SUBMIT_DATE, "timestamp");
		appendField(sb, Schema.COLUMN_DELIVERY_DATE, "timestamp");

		appendField(sb, Schema.COLUMN_SERVICE_TYPE, "text");
		appendField(sb, Schema.COLUMN_ESM_CLASS, "int");
		appendField(sb, Schema.COLUMN_PROTOCOL_ID, "int");
		appendField(sb, Schema.COLUMN_PRIORITY, "int");
		appendField(sb, Schema.COLUMN_REGISTERED_DELIVERY, "int");
		appendField(sb, Schema.COLUMN_REPLACE, "int");
		appendField(sb, Schema.COLUMN_DATA_CODING, "int");
		appendField(sb, Schema.COLUMN_DEFAULT_MSG_ID, "int");

        appendField(sb, Schema.COLUMN_MESSAGE, "blob");
        appendField(sb, Schema.COLUMN_MESSAGE_TEXT, "text");
        appendField(sb, Schema.COLUMN_MESSAGE_BIN, "blob");
		appendField(sb, Schema.COLUMN_OPTIONAL_PARAMETERS, "text");
		appendField(sb, Schema.COLUMN_SCHEDULE_DELIVERY_TIME, "timestamp");
		appendField(sb, Schema.COLUMN_VALIDITY_PERIOD, "timestamp");

        appendField(sb, Schema.COLUMN_IMSI, "ascii");
        appendField(sb, Schema.COLUMN_CORR_ID, "ascii");
		appendField(sb, Schema.COLUMN_NNN_DIGITS, "ascii");
		appendField(sb, Schema.COLUMN_NNN_AN, "int");
		appendField(sb, Schema.COLUMN_NNN_NP, "int");
		appendField(sb, Schema.COLUMN_SM_STATUS, "int");
		appendField(sb, Schema.COLUMN_SM_TYPE, "int");
		appendField(sb, Schema.COLUMN_DELIVERY_COUNT, "int");
	}

	private synchronized void checkCurrentSlotTableExists() throws PersistenceException {
		try {
			try {
				// checking of CURRENT_SLOT_TABLE existence
				String sa = "SELECT \"" + Schema.COLUMN_NEXT_SLOT + "\" FROM \"" + Schema.FAMILY_CURRENT_SLOT_TABLE
						+ "\" where \"" + Schema.COLUMN_ID + "\"=0;";
				PreparedStatement ps = session.prepare(sa);
			} catch (InvalidQueryException e) {
				StringBuilder sb = new StringBuilder();
				sb.append("CREATE TABLE \"");
				sb.append(Schema.FAMILY_CURRENT_SLOT_TABLE);
				sb.append("\" (");

				appendField(sb, Schema.COLUMN_ID, "int");
				appendField(sb, Schema.COLUMN_NEXT_SLOT, "bigint");

				sb.append("PRIMARY KEY (\"");
				sb.append(Schema.COLUMN_ID);
				sb.append("\"");
				sb.append("));");

				String s2 = sb.toString();
				PreparedStatement ps = session.prepare(s2);
				BoundStatement boundStatement = new BoundStatement(ps);
				ResultSet res = session.execute(boundStatement);
			}
		} catch (Exception e1) {
			String msg = "Failed to access or create table " + Schema.FAMILY_CURRENT_SLOT_TABLE + "!";
			throw new PersistenceException(msg, e1);
		}

		try {
			try {
				// checking of SMPP_SMS_ROUTING_RULE_2 existence
                String sa = "SELECT \"" + Schema.COLUMN_ADDRESS + "\" FROM \"" + Schema.FAMILY_SMPP_SMS_ROUTING_RULE + "\" where \"" + Schema.COLUMN_ADDRESS
                        + "\"='' and \"" + Schema.COLUMN_NETWORK_ID + "\"=0;";
                PreparedStatement ps = session.prepare(sa);
			} catch (InvalidQueryException e) {
				StringBuilder sb = new StringBuilder();
				sb.append("CREATE TABLE \"");
				sb.append(Schema.FAMILY_SMPP_SMS_ROUTING_RULE);
				sb.append("\" (");

                appendField(sb, Schema.COLUMN_ADDRESS, "text");
                appendField(sb, Schema.COLUMN_NETWORK_ID, "int");
				appendField(sb, Schema.COLUMN_CLUSTER_NAME, "text");

				sb.append("PRIMARY KEY (\"");
                sb.append(Schema.COLUMN_ADDRESS);
                sb.append("\", \"");
                sb.append(Schema.COLUMN_NETWORK_ID);
				sb.append("\"");
				sb.append("));");

				String s2 = sb.toString();
				PreparedStatement ps = session.prepare(s2);
				BoundStatement boundStatement = new BoundStatement(ps);
				ResultSet res = session.execute(boundStatement);
			}
		} catch (Exception e1) {
			String msg = "Failed to access or create table " + Schema.FAMILY_SMPP_SMS_ROUTING_RULE + "!";
			throw new PersistenceException(msg, e1);
		}

		try {
			try {
				// checking of SIP_SMS_ROUTING_RULE_2 existence
                String sa = "SELECT \"" + Schema.COLUMN_ADDRESS + "\" FROM \"" + Schema.FAMILY_SIP_SMS_ROUTING_RULE + "\" where \"" + Schema.COLUMN_ADDRESS
                        + "\"='' and \"" + Schema.COLUMN_NETWORK_ID + "\"=0;";
				PreparedStatement ps = session.prepare(sa);
			} catch (InvalidQueryException e) {
				StringBuilder sb = new StringBuilder();
				sb.append("CREATE TABLE \"");
				sb.append(Schema.FAMILY_SIP_SMS_ROUTING_RULE);
				sb.append("\" (");

				appendField(sb, Schema.COLUMN_ADDRESS, "text");
                appendField(sb, Schema.COLUMN_NETWORK_ID, "int");
				appendField(sb, Schema.COLUMN_CLUSTER_NAME, "text");

				sb.append("PRIMARY KEY (\"");
				sb.append(Schema.COLUMN_ADDRESS);
                sb.append("\", \"");
                sb.append(Schema.COLUMN_NETWORK_ID);
				sb.append("\"");
				sb.append("));");

				String s2 = sb.toString();
				PreparedStatement ps = session.prepare(s2);
				BoundStatement boundStatement = new BoundStatement(ps);
				ResultSet res = session.execute(boundStatement);
			}
		} catch (Exception e1) {
			String msg = "Failed to access or create table " + Schema.FAMILY_SIP_SMS_ROUTING_RULE + "!";
			throw new PersistenceException(msg, e1);
		}
	}

	protected void appendField(StringBuilder sb, String name, String type) {
		sb.append("\"");
		sb.append(name);
		sb.append("\" ");
		sb.append(type);
		sb.append(", ");
	}

	private void appendIndex(String tName, String fieldName) {
		StringBuilder sb = new StringBuilder();
		sb.append("CREATE INDEX ON \"");
		sb.append(tName);
		sb.append("\" (\"");
		sb.append(fieldName);
		sb.append("\");");

		String s2 = sb.toString();
		PreparedStatement ps = session.prepare(s2);
		BoundStatement boundStatement = new BoundStatement(ps);
		ResultSet res = session.execute(boundStatement);
	}

	/**
	 * Returns the SMPP Cluster name for passed address. If not found returns
	 * null
	 * 
	 * @param address
	 * @return
	 * @throws PersistenceException
	 */
	public DbSmsRoutingRule c2_getSmppSmsRoutingRule(final String address, int networkId) throws PersistenceException {

		try {
			BoundStatement boundStatement = new BoundStatement(getSmppSmsRoutingRule);
			boundStatement.bind(address, networkId);
			ResultSet result = session.execute(boundStatement);

			Row row = result.one();
			if (row == null) {
				return null;
			} else {
				String name = row.getString(Schema.COLUMN_CLUSTER_NAME);
				DbSmsRoutingRule res = new DbSmsRoutingRule(SmsRoutingRuleType.SMPP, address, networkId, name);
				return res;
			}
		} catch (Exception e) {
            String msg = "Failed to getSmppSmsRoutingRule for address=" + address + " networkId=" + networkId + " !";
			throw new PersistenceException(msg, e);
		}
	}

	/**
	 * Returns SIP Cluster name for passed address. If not found returns null
	 * 
	 * @param address
	 * @return
	 * @throws PersistenceException
	 */
	public DbSmsRoutingRule c2_getSipSmsRoutingRule(final String address, int networkId) throws PersistenceException {

		try {
			BoundStatement boundStatement = new BoundStatement(getSipSmsRoutingRule);
			boundStatement.bind(address, networkId);
			ResultSet result = session.execute(boundStatement);

			Row row = result.one();
			if (row == null) {
				return null;
			} else {
				String name = row.getString(Schema.COLUMN_CLUSTER_NAME);
				DbSmsRoutingRule res = new DbSmsRoutingRule(SmsRoutingRuleType.SIP, address, networkId, name);
				return res;
			}
		} catch (Exception e) {
            String msg = "Failed to getSipSmsRoutingRule for address=" + address + " networkId=" + networkId + " !";
			throw new PersistenceException(msg, e);
		}
	}

	/**
	 * Updates the SMPP_SMS_ROUTING_RULE table, sets the CLUSTER_NAME for passed
	 * ADDRESS
	 * 
	 * @param dbSmsRoutingRule
	 * @throws PersistenceException
	 */
	public void c2_updateSmppSmsRoutingRule(DbSmsRoutingRule dbSmsRoutingRule) throws PersistenceException {
		try {
			BoundStatement boundStatement = new BoundStatement(updateSmppSmsRoutingRule);
			boundStatement.bind(dbSmsRoutingRule.getAddress(), dbSmsRoutingRule.getNetworkId(), dbSmsRoutingRule.getClusterName());
			session.execute(boundStatement);
		} catch (Exception e) {
            String msg = "Failed to updateSmppSmsRoutingRule for address=" + dbSmsRoutingRule.getAddress() + " networkId=" + dbSmsRoutingRule.getNetworkId() + " !";
			throw new PersistenceException(msg, e);
		}
	}

	/**
	 * Updates the SIP_SMS_ROUTING_RULE table, sets the CLUSTER_NAME for passed
	 * ADDRESS
	 * 
	 * @param dbSmsRoutingRule
	 * @throws PersistenceException
	 */
	public void c2_updateSipSmsRoutingRule(DbSmsRoutingRule dbSmsRoutingRule) throws PersistenceException {
		try {
			BoundStatement boundStatement = new BoundStatement(updateSipSmsRoutingRule);
            boundStatement.bind(dbSmsRoutingRule.getAddress(), dbSmsRoutingRule.getNetworkId(), dbSmsRoutingRule.getClusterName());
			session.execute(boundStatement);
		} catch (Exception e) {
            String msg = "Failed to updateSipSmsRoutingRule for address=" + dbSmsRoutingRule.getAddress() + " networkId=" + dbSmsRoutingRule.getNetworkId() + " !";
			throw new PersistenceException(msg, e);
		}
	}

	/**
	 * Deletes row corresponding to passed address from SMPP_SMS_ROUTING_RULE
	 * 
	 * @param address
	 * @throws PersistenceException
	 */
	public void c2_deleteSmppSmsRoutingRule(final String address, int networkId) throws PersistenceException {
		try {
			BoundStatement boundStatement = new BoundStatement(deleteSmppSmsRoutingRule);
			boundStatement.bind(address, networkId);
			session.execute(boundStatement);
		} catch (Exception e) {
            String msg = "Failed to deleteSmppSmsRoutingRule for '" + address + "-" + networkId + "'!";
			throw new PersistenceException(msg, e);
		}
	}

	/**
	 * Deletes row corresponding to passed address from SIP_SMS_ROUTING_RULE
	 * 
	 * @param address
	 * @throws PersistenceException
	 */
	public void c2_deleteSipSmsRoutingRule(final String address, int networkId) throws PersistenceException {
		try {
			BoundStatement boundStatement = new BoundStatement(deleteSipSmsRoutingRule);
			boundStatement.bind(address, networkId);
			session.execute(boundStatement);
		} catch (Exception e) {
            String msg = "Failed to deleteSipSmsRoutingRule for '" + address + "-" + networkId + "'!";
			throw new PersistenceException(msg, e);
		}
	}

	/**
	 * Returns first 100 rows of SMPP_SMS_ROUTING_RULE
	 * 
	 * @return
	 * @throws PersistenceException
	 */
	public List c2_getSmppSmsRoutingRulesRange() throws PersistenceException {
		return c2_getSmppSmsRoutingRulesRange(null);
	}

	/**
	 * Returns 100 rows of SMPP_SMS_ROUTING_RULE starting from passed
	 * lastAdress. If lastAdress, first 100 rows are returned
	 * 
	 * @param lastAdress
	 * @return
	 * @throws PersistenceException
	 */
	public List c2_getSmppSmsRoutingRulesRange(String lastAdress) throws PersistenceException {

		List ress = new FastList();
		try {
			PreparedStatement ps = lastAdress != null ? getSmppSmsRoutingRulesRange : getSmppSmsRoutingRulesRange2;
			BoundStatement boundStatement = new BoundStatement(ps);
			if (lastAdress != null) {
				boundStatement.bind(lastAdress);
			}
			ResultSet result = session.execute(boundStatement);

			int i1 = 0;
			for (Row row : result) {
				String address = row.getString(Schema.COLUMN_ADDRESS);
                String name = row.getString(Schema.COLUMN_CLUSTER_NAME);
                int networkId = row.getInt(Schema.COLUMN_NETWORK_ID);

				DbSmsRoutingRule res = new DbSmsRoutingRule(SmsRoutingRuleType.SMPP, address, networkId, name);

				if (i1 == 0) {
					i1 = 1;
					if (lastAdress == null)
						ress.add(res);
				} else {
					ress.add(res);
				}
			}

			return ress;
		} catch (Exception e) {
			String msg = "Failed to getSmsRoutingRule DbSmsRoutingRule for all records: " + e;

			throw new PersistenceException(msg, e);
		}
	}

	/**
	 * Returns first 100 rows of SIP_SMS_ROUTING_RULE
	 * 
	 * @return
	 * @throws PersistenceException
	 */
	public List c2_getSipSmsRoutingRulesRange() throws PersistenceException {
		return c2_getSipSmsRoutingRulesRange(null);
	}

	/**
	 * Returns 100 rows of SIP_SMS_ROUTING_RULE starting from passed lastAdress.
	 * If lastAdress, first 100 rows are returned
	 * 
	 * @param lastAdress
	 * @return
	 * @throws PersistenceException
	 */
	public List c2_getSipSmsRoutingRulesRange(String lastAdress) throws PersistenceException {

		List ress = new FastList();
		try {
			PreparedStatement ps = lastAdress != null ? getSipSmsRoutingRulesRange : getSipSmsRoutingRulesRange2;
			BoundStatement boundStatement = new BoundStatement(ps);
			if (lastAdress != null) {
				boundStatement.bind(lastAdress);
			}
			ResultSet result = session.execute(boundStatement);

			int i1 = 0;
			for (Row row : result) {
				String address = row.getString(Schema.COLUMN_ADDRESS);
				String name = row.getString(Schema.COLUMN_CLUSTER_NAME);
                int networkId = row.getInt(Schema.COLUMN_NETWORK_ID);

				DbSmsRoutingRule res = new DbSmsRoutingRule(SmsRoutingRuleType.SIP, address, networkId, name);

				if (i1 == 0) {
					i1 = 1;
					if (lastAdress == null)
						ress.add(res);
				} else {
					ress.add(res);
				}
			}

			return ress;
		} catch (Exception e) {
			String msg = "Failed to getSmsRoutingRule DbSmsRoutingRule for all records: " + e;

			throw new PersistenceException(msg, e);
		}
	}

    /**
     * Deleting live tables (DST_SLOT_TABLE_YYYY_MM_DD and
     * SLOT_MESSAGES_TABLE_YYYY_MM_DD) for a defined date. Before deleting
     * checking is made for we can not delay table for future, today and 2 days
     * before
     * 
     * @param dt
     *            Date for a table
     * @return true: success or perm failure, false: temporary failure, we need
     *         to retry
     */
    public boolean c2_deleteLiveTablesForDate(Date dt) {
        // auto_snapshot option !!!

        CheckDeletingDateResult res = checkDeletingDate(dt, "live cassandra tables");
        switch (res) {
        case permFailure:
            return true;
        case tempFailure:
            return false;
        }

        String tName = "DST_SLOT_TABLE" + getTableName(dt);
        this.doDeleteTable(tName);

        tName = "SLOT_MESSAGES_TABLE" + getTableName(dt);
        this.doDeleteTable(tName);

        String tName2 = this.getTableName(dt);
        dataTableRead.remove(tName2);

        return true;
    }

    /**
     * Deleting live table (MESSAGES) for a defined date. Before deleting
     * checking is made for we can not delay table for future, today and 2 days
     * before
     *
     * @param dt
     *            Date for a table
     * @return true: success or perm failure, false: temporary failure, we need
     *         to retry
     */
    public boolean c2_deleteArchiveTablesForDate(Date dt) {
        // auto_snapshot option !!!

        CheckDeletingDateResult res = checkDeletingDate(dt, "archive cassandra tables");
        switch (res) {
        case permFailure:
            return true;
        case tempFailure:
            return false;
        }

        String tName = "MESSAGES" + getTableName(dt);
        this.doDeleteTable(tName);

        String tName2 = this.getTableName(dt);
        dataTableRead.remove(tName2);

        return true;
    }

    private CheckDeletingDateResult checkDeletingDate(Date dt, String taskName) {
        // checking date
        int maxBackupsDays = 2;
        Date curTime = new Date();
        Date curDate = new Date(curTime.getYear(), curTime.getMonth(), curTime.getDate());
        Date maxDate = new Date(curDate.getTime() - maxBackupsDays * 24 * 3600 * 1000);
        if (!dt.before(maxDate)) {
            logger.warn("Rejected an attempt of dropping of " + taskName + " for too late date: " + dt);
            return CheckDeletingDateResult.permFailure;
        }

        // checking if this date is still in processing
        Date procDate = this.c2_getTimeForDueSlot(this.c2_getCurrentDueSlot());
        if (procDate.getYear() != curDate.getYear() || procDate.getMonth() != curDate.getMonth() || procDate.getDate() != curDate.getDate()) {
            logger.warn("Rejected an attempt of dropping of " + taskName + " for old message data are still in processing: " + dt);
            return CheckDeletingDateResult.tempFailure;
        }

        return CheckDeletingDateResult.success;
    }

    enum CheckDeletingDateResult {
        success, permFailure, tempFailure;
    }

    private void doDeleteTable(String tName) {
        try {
            String sa = "select * from \"" + tName + "\" limit 1";
            PreparedStatement ps = session.prepare(sa);
            BoundStatement boundStatement = new BoundStatement(ps);
            session.execute(boundStatement);
        } catch (Exception e) {
            logger.info("Can not drop cassandra table because it is absent: " + tName);
            return;
        }

        try {
            String sb = "DROP TABLE \"" + tName + "\"";
            PreparedStatement ps = session.prepare(sb);
            BoundStatement boundStatement = new BoundStatement(ps);
            session.execute(boundStatement);

            logger.warn("Successfully dropped cassandra table: " + tName);
        } catch (Exception e) {
            logger.warn("Exception when dropping cassandra table: " + tName, e);
        }
    }

    public Date[] c2_getLiveTableList(String keyspace) {
        String[] ss = this.c2_getTableList(keyspace);

        FastMap res = new FastMap();
        for (String s : ss) {
            Date dt = null;
            if (s.startsWith("DST_SLOT_TABLE_") && s.length() == 25) {
                String sYear = s.substring(15, 19);
                String sMon = s.substring(20, 22);
                String sDay = s.substring(23, 25);
                try {
                    int year = Integer.parseInt(sYear);
                    int mon = Integer.parseInt(sMon);
                    int day = Integer.parseInt(sDay);
                    dt = new Date(year - 1900, mon - 1, day);
                } catch (Exception e) {
                }
            }
            if (s.startsWith("SLOT_MESSAGES_TABLE_") && s.length() == 30) {
                String sYear = s.substring(20, 24);
                String sMon = s.substring(25, 27);
                String sDay = s.substring(28, 30);
                try {
                    int year = Integer.parseInt(sYear);
                    int mon = Integer.parseInt(sMon);
                    int day = Integer.parseInt(sDay);
                    dt = new Date(year - 1900, mon - 1, day);
                } catch (Exception e) {
                }
            }
            if (dt != null) {
                res.put(dt, dt);
            }
        }

        Date[] dd = new Date[res.size()];
        int i1 = 0;
        for (Date dt : res.keySet()) {
            dd[i1++] = dt;
        }
        Arrays.sort(dd);

        return dd;
    }

    public Date[] c2_getArchiveTableList(String keyspace) {
        String[] ss = this.c2_getTableList(keyspace);

        FastMap res = new FastMap();
        for (String s : ss) {
            Date dt = null;
            if (s.startsWith("MESSAGES_") && s.length() == 19) {
                String sYear = s.substring(9, 13);
                String sMon = s.substring(14, 16);
                String sDay = s.substring(17, 19);
                try {
                    int year = Integer.parseInt(sYear);
                    int mon = Integer.parseInt(sMon);
                    int day = Integer.parseInt(sDay);
                    dt = new Date(year - 1900, mon - 1, day);
                } catch (Exception e) {
                }
            }
            if (dt != null) {
                res.put(dt, dt);
            }
        }

        Date[] dd = new Date[res.size()];
        int i1 = 0;
        for (Date dt : res.keySet()) {
            dd[i1++] = dt;
        }
        Arrays.sort(dd);

        return dd;
    }

    public String[] c2_getTableList(String keyspace) {
        List lst = this.cluster.getMetadata().getKeyspaces();
        String[] ss = new String[0];
        for (KeyspaceMetadata km : lst) {
            if (km.getName().equals(keyspace)) {
                Collection lstTableMetadata = km.getTables();
                ss = new String[lstTableMetadata.size()];
                int i1 = 0;
                for (TableMetadata tm : lstTableMetadata) {
                    ss[i1++] = tm.getName();
                }
                break;
            }
        }
        return ss;


//        ArrayList res = new ArrayList();
//        try {
//            
//            BoundStatement boundStatement = new BoundStatement(this.getTableList);
//
//            ResultSet result = session.execute(boundStatement);
//
//            for (Row row : result) {
//                String keyspace1 = row.getString(Schema.COLUMN_SYSTEM_KEYSPACE_NAME);
//                String tableName = row.getString(Schema.COLUMN_SYSTEM_COLUMNFAMILY_NAME);
//
//                if (keyspace.equals(keyspace1)) {
//                    res.add(tableName);
//                }
//            }
//        } catch (Exception e) {
//            logger.info("Can not get a cassandra table list");
//            return new String[0];
//        }
//
//        String[] ss = new String[res.size()];
//        res.toArray(ss);
//        return ss;
    }

	private class DueSlotWritingElement {
		public long dueSlot;
		public int writingCount;
		public Date lastStartDate;

		public DueSlotWritingElement(long dueSlot) {
			this.dueSlot = dueSlot;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy