mirror of
https://github.com/Telecominfraproject/wlan-cloud-services.git
synced 2025-11-02 03:27:54 +00:00
[WIFI-2068] Change client session TTL to 24 hours
This commit is contained in:
1
adoption-metrics-datastore-common-test/.gitignore
vendored
Normal file
1
adoption-metrics-datastore-common-test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
adoption-metrics-datastore-inmemory/.gitignore
vendored
Normal file
1
adoption-metrics-datastore-inmemory/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
adoption-metrics-datastore-interface/.gitignore
vendored
Normal file
1
adoption-metrics-datastore-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
adoption-metrics-datastore-rdbms/.gitignore
vendored
Normal file
1
adoption-metrics-datastore-rdbms/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
adoption-metrics-models/.gitignore
vendored
Normal file
1
adoption-metrics-models/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
adoption-metrics-service-interface/.gitignore
vendored
Normal file
1
adoption-metrics-service-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
adoption-metrics-service-local/.gitignore
vendored
Normal file
1
adoption-metrics-service-local/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
adoption-metrics-service-remote/.gitignore
vendored
Normal file
1
adoption-metrics-service-remote/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
adoption-metrics-service/.gitignore
vendored
Normal file
1
adoption-metrics-service/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
adoption-metrics-sp/.gitignore
vendored
Normal file
1
adoption-metrics-sp/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
alarm-datastore-cassandra/.gitignore
vendored
Normal file
1
alarm-datastore-cassandra/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
alarm-datastore-common-test/.gitignore
vendored
Normal file
1
alarm-datastore-common-test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
alarm-datastore-inmemory/.gitignore
vendored
Normal file
1
alarm-datastore-inmemory/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
alarm-datastore-interface/.gitignore
vendored
Normal file
1
alarm-datastore-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
alarm-datastore-rdbms/.gitignore
vendored
Normal file
1
alarm-datastore-rdbms/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
alarm-models/.gitignore
vendored
Normal file
1
alarm-models/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
alarm-service-interface/.gitignore
vendored
Normal file
1
alarm-service-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
alarm-service-local/.gitignore
vendored
Normal file
1
alarm-service-local/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
alarm-service-remote/.gitignore
vendored
Normal file
1
alarm-service-remote/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
alarm-service/.gitignore
vendored
Normal file
1
alarm-service/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
all-cloud-and-opensync-gw-in-one-process/.gitignore
vendored
Normal file
1
all-cloud-and-opensync-gw-in-one-process/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
all-cloud-in-one-process-with-persistence/.gitignore
vendored
Normal file
1
all-cloud-in-one-process-with-persistence/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
@@ -279,18 +279,6 @@
|
|||||||
<version>1.1.0-SNAPSHOT</version>
|
<version>1.1.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<artifactId>client-service</artifactId>
|
|
||||||
<groupId>com.telecominfraproject.wlan</groupId>
|
|
||||||
<version>1.1.0-SNAPSHOT</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<artifactId>client-datastore-rdbms</artifactId>
|
|
||||||
<groupId>com.telecominfraproject.wlan</groupId>
|
|
||||||
<version>1.1.0-SNAPSHOT</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<artifactId>routing-service</artifactId>
|
<artifactId>routing-service</artifactId>
|
||||||
<groupId>com.telecominfraproject.wlan</groupId>
|
<groupId>com.telecominfraproject.wlan</groupId>
|
||||||
|
|||||||
1
all-cloud-in-one-process/.gitignore
vendored
Normal file
1
all-cloud-in-one-process/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
client-datastore-cassandra/.gitignore
vendored
Normal file
1
client-datastore-cassandra/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
@@ -42,8 +42,8 @@ CREATE TABLE IF NOT EXISTS tip_wlan_keyspace.client_session (
|
|||||||
details blob,
|
details blob,
|
||||||
|
|
||||||
PRIMARY KEY ((customerId, macAddress), equipmentId)
|
PRIMARY KEY ((customerId, macAddress), equipmentId)
|
||||||
) WITH comment='Wireless client sessions used by TIP WLAN CloudSDK. Records automatically expire after 30 days'
|
) WITH comment='Wireless client sessions used by TIP WLAN CloudSDK. Records automatically expire after 24 hours'
|
||||||
AND default_time_to_live = 2592000;
|
AND default_time_to_live = 86400;
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_client_session_customerId ON tip_wlan_keyspace.client_session (customerId);
|
CREATE INDEX IF NOT EXISTS idx_client_session_customerId ON tip_wlan_keyspace.client_session (customerId);
|
||||||
|
|
||||||
@@ -53,8 +53,8 @@ CREATE TABLE IF NOT EXISTS tip_wlan_keyspace.client_session_by_equipment (
|
|||||||
macAddress bigint ,
|
macAddress bigint ,
|
||||||
|
|
||||||
PRIMARY KEY ((customerId, equipmentId), macAddress)
|
PRIMARY KEY ((customerId, equipmentId), macAddress)
|
||||||
) WITH comment='Index Table to look up wireless client sessions by equipmentId used by TIP WLAN CloudSDK. Records automatically expire after 30 days'
|
) WITH comment='Index Table to look up wireless client sessions by equipmentId used by TIP WLAN CloudSDK. Records automatically expire after 24 hours'
|
||||||
AND default_time_to_live = 2592000;
|
AND default_time_to_live = 86400;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tip_wlan_keyspace.client_session_by_location (
|
CREATE TABLE IF NOT EXISTS tip_wlan_keyspace.client_session_by_location (
|
||||||
customerId int,
|
customerId int,
|
||||||
@@ -63,8 +63,8 @@ CREATE TABLE IF NOT EXISTS tip_wlan_keyspace.client_session_by_location (
|
|||||||
macAddress bigint,
|
macAddress bigint,
|
||||||
|
|
||||||
PRIMARY KEY ((locationId), equipmentId, macAddress)
|
PRIMARY KEY ((locationId), equipmentId, macAddress)
|
||||||
) WITH comment='Index Table to look up wireless client sessions by location and equipment used by TIP WLAN CloudSDK. Records automatically expire after 30 days'
|
) WITH comment='Index Table to look up wireless client sessions by location and equipment used by TIP WLAN CloudSDK. Records automatically expire after 24 hours'
|
||||||
AND default_time_to_live = 2592000;
|
AND default_time_to_live = 86400;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tip_wlan_keyspace.client_session_by_mac (
|
CREATE TABLE IF NOT EXISTS tip_wlan_keyspace.client_session_by_mac (
|
||||||
customerId int,
|
customerId int,
|
||||||
@@ -74,8 +74,8 @@ CREATE TABLE IF NOT EXISTS tip_wlan_keyspace.client_session_by_mac (
|
|||||||
macAddressString text,
|
macAddressString text,
|
||||||
|
|
||||||
PRIMARY KEY ((macAddress), locationId, equipmentId, macAddressString)
|
PRIMARY KEY ((macAddress), locationId, equipmentId, macAddressString)
|
||||||
) WITH comment='Index Table to look up wireless client sessions by macAddressString, location, and equipment by TIP WLAN CloudSDK. Records automatically expire after 30 days'
|
) WITH comment='Index Table to look up wireless client sessions by macAddressString, location, and equipment by TIP WLAN CloudSDK. Records automatically expire after 24 hours'
|
||||||
AND default_time_to_live = 2592000;
|
AND default_time_to_live = 86400;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tip_wlan_keyspace.client_session_by_mac_and_equipment (
|
CREATE TABLE IF NOT EXISTS tip_wlan_keyspace.client_session_by_mac_and_equipment (
|
||||||
customerId int,
|
customerId int,
|
||||||
@@ -85,8 +85,8 @@ CREATE TABLE IF NOT EXISTS tip_wlan_keyspace.client_session_by_mac_and_equipment
|
|||||||
macAddressString text,
|
macAddressString text,
|
||||||
|
|
||||||
PRIMARY KEY ((macAddress), equipmentId, locationId, macAddressString)
|
PRIMARY KEY ((macAddress), equipmentId, locationId, macAddressString)
|
||||||
) WITH comment='Index Table to look up wireless client sessions by macAddressString, location, and equipment by TIP WLAN CloudSDK. Records automatically expire after 30 days'
|
) WITH comment='Index Table to look up wireless client sessions by macAddressString, location, and equipment by TIP WLAN CloudSDK. Records automatically expire after 24 hours'
|
||||||
AND default_time_to_live = 2592000;
|
AND default_time_to_live = 86400;
|
||||||
|
|
||||||
DROP INDEX IF EXISTS tip_wlan_keyspace.idx_client_session_by_mac_macAddressString;
|
DROP INDEX IF EXISTS tip_wlan_keyspace.idx_client_session_by_mac_macAddressString;
|
||||||
DROP INDEX IF EXISTS tip_wlan_keyspace.idx_client_session_by_mac_and_equipment_macAddressString;
|
DROP INDEX IF EXISTS tip_wlan_keyspace.idx_client_session_by_mac_and_equipment_macAddressString;
|
||||||
|
|||||||
1
client-datastore-common-test/.gitignore
vendored
Normal file
1
client-datastore-common-test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
client-datastore-inmemory/.gitignore
vendored
Normal file
1
client-datastore-inmemory/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
client-datastore-interface/.gitignore
vendored
Normal file
1
client-datastore-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>com.telecominfraproject.wlan</groupId>
|
|
||||||
<artifactId>tip-wlan-cloud-root-pom</artifactId>
|
|
||||||
<version>1.1.0-SNAPSHOT</version>
|
|
||||||
<relativePath>../../wlan-cloud-root</relativePath>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>client-datastore-rdbms</artifactId>
|
|
||||||
<name>client-datastore-rdbms</name>
|
|
||||||
<description>SQL implementation of the data store</description>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.telecominfraproject.wlan</groupId>
|
|
||||||
<artifactId>base-jdbc</artifactId>
|
|
||||||
<version>1.1.0-SNAPSHOT</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.telecominfraproject.wlan</groupId>
|
|
||||||
<artifactId>client-datastore-interface</artifactId>
|
|
||||||
<version>1.1.0-SNAPSHOT</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.telecominfraproject.wlan</groupId>
|
|
||||||
<artifactId>base-jdbc-tests</artifactId>
|
|
||||||
<version>1.1.0-SNAPSHOT</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.telecominfraproject.wlan</groupId>
|
|
||||||
<artifactId>client-datastore-common-test</artifactId>
|
|
||||||
<version>1.1.0-SNAPSHOT</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
@@ -1,493 +0,0 @@
|
|||||||
package com.telecominfraproject.wlan.client.datastore.rdbms;
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.dao.DuplicateKeyException;
|
|
||||||
import org.springframework.dao.EmptyResultDataAccessException;
|
|
||||||
import org.springframework.jdbc.core.PreparedStatementCreator;
|
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.client.info.models.ClientInfoDetails;
|
|
||||||
import com.telecominfraproject.wlan.client.models.Client;
|
|
||||||
import com.telecominfraproject.wlan.core.model.equipment.MacAddress;
|
|
||||||
import com.telecominfraproject.wlan.core.model.pagination.ColumnAndSort;
|
|
||||||
import com.telecominfraproject.wlan.core.model.pagination.PaginationContext;
|
|
||||||
import com.telecominfraproject.wlan.core.model.pagination.PaginationResponse;
|
|
||||||
import com.telecominfraproject.wlan.core.model.pagination.SortOrder;
|
|
||||||
import com.telecominfraproject.wlan.core.server.jdbc.BaseJdbcDao;
|
|
||||||
import com.telecominfraproject.wlan.datastore.exceptions.DsConcurrentModificationException;
|
|
||||||
import com.telecominfraproject.wlan.datastore.exceptions.DsDuplicateEntityException;
|
|
||||||
import com.telecominfraproject.wlan.datastore.exceptions.DsEntityNotFoundException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author dtoptygin
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Repository
|
|
||||||
@Transactional(propagation = Propagation.MANDATORY)
|
|
||||||
public class ClientDAO extends BaseJdbcDao {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ClientDatastoreRdbms.class);
|
|
||||||
|
|
||||||
private static final String COL_ID = "macAddress";
|
|
||||||
|
|
||||||
private static final String[] GENERATED_KEY_COLS = { };
|
|
||||||
|
|
||||||
private static final String[] ALL_COLUMNS_LIST = {
|
|
||||||
COL_ID,
|
|
||||||
"macAddressString",
|
|
||||||
|
|
||||||
//TODO: add colums from properties Client in here
|
|
||||||
"customerId",
|
|
||||||
"details",
|
|
||||||
//make sure the order of properties matches this list and list in ClientRowMapper and list in create/update methods
|
|
||||||
|
|
||||||
"createdTimestamp",
|
|
||||||
"lastModifiedTimestamp"
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final Set<String> columnsToSkipForInsert = new HashSet<>(Arrays.asList());
|
|
||||||
private static final Set<String> columnsToSkipForUpdate = new HashSet<>(Arrays.asList(COL_ID, "macAddressString", "createdTimestamp", "customerId"));
|
|
||||||
|
|
||||||
private static final String TABLE_NAME = "client";
|
|
||||||
private static final String TABLE_PREFIX = "c.";
|
|
||||||
private static final String ALL_COLUMNS;
|
|
||||||
|
|
||||||
private static final Set<String> ALL_COLUMNS_LOWERCASE = new HashSet<>();
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
//use this for queries where multiple tables are involved
|
|
||||||
private static final String ALL_COLUMNS_WITH_PREFIX;
|
|
||||||
|
|
||||||
private static final String ALL_COLUMNS_FOR_INSERT;
|
|
||||||
private static final String BIND_VARS_FOR_INSERT;
|
|
||||||
private static final String ALL_COLUMNS_UPDATE;
|
|
||||||
|
|
||||||
static{
|
|
||||||
StringBuilder strbAllColumns = new StringBuilder(1024);
|
|
||||||
StringBuilder strbAllColumnsWithPrefix = new StringBuilder(1024);
|
|
||||||
StringBuilder strbAllColumnsForInsert = new StringBuilder(1024);
|
|
||||||
StringBuilder strbBindVarsForInsert = new StringBuilder(128);
|
|
||||||
StringBuilder strbColumnsForUpdate = new StringBuilder(512);
|
|
||||||
for(String colName: ALL_COLUMNS_LIST){
|
|
||||||
|
|
||||||
ALL_COLUMNS_LOWERCASE.add(colName.toLowerCase());
|
|
||||||
|
|
||||||
strbAllColumns.append(colName).append(",");
|
|
||||||
strbAllColumnsWithPrefix.append(TABLE_PREFIX).append(colName).append(",");
|
|
||||||
|
|
||||||
if(!columnsToSkipForInsert.contains(colName)){
|
|
||||||
strbAllColumnsForInsert.append(colName).append(",");
|
|
||||||
strbBindVarsForInsert.append("?,");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!columnsToSkipForUpdate.contains(colName)){
|
|
||||||
strbColumnsForUpdate.append(colName).append("=?,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove trailing ','
|
|
||||||
strbAllColumns.deleteCharAt(strbAllColumns.length() - 1);
|
|
||||||
strbAllColumnsWithPrefix.deleteCharAt(strbAllColumnsWithPrefix.length() - 1);
|
|
||||||
strbAllColumnsForInsert.deleteCharAt(strbAllColumnsForInsert.length() - 1);
|
|
||||||
strbBindVarsForInsert.deleteCharAt(strbBindVarsForInsert.length() - 1);
|
|
||||||
strbColumnsForUpdate.deleteCharAt(strbColumnsForUpdate.length() - 1);
|
|
||||||
|
|
||||||
ALL_COLUMNS = strbAllColumns.toString();
|
|
||||||
ALL_COLUMNS_WITH_PREFIX = strbAllColumnsWithPrefix.toString();
|
|
||||||
ALL_COLUMNS_FOR_INSERT = strbAllColumnsForInsert.toString();
|
|
||||||
BIND_VARS_FOR_INSERT = strbBindVarsForInsert.toString();
|
|
||||||
ALL_COLUMNS_UPDATE = strbColumnsForUpdate.toString();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static final String SQL_GET_BY_ID =
|
|
||||||
"select " + ALL_COLUMNS +
|
|
||||||
" from "+TABLE_NAME+" " +
|
|
||||||
" where customerId = ? and " + COL_ID + " = ?";
|
|
||||||
|
|
||||||
private static final String SQL_GET_BY_CUSTOMER_ID =
|
|
||||||
"select " + ALL_COLUMNS +
|
|
||||||
" from " + TABLE_NAME + " " +
|
|
||||||
" where customerId = ? ";
|
|
||||||
|
|
||||||
private static final String SQL_GET_BLOCKED_CLIENTS = "select " + ALL_COLUMNS_WITH_PREFIX +
|
|
||||||
" from " + TABLE_NAME + " c , client_blocklist cb " +
|
|
||||||
" where cb.customerId = ? and c.customerId = cb.customerId and c.macAddress = cb.macAddress ";
|
|
||||||
|
|
||||||
|
|
||||||
private static final String SQL_GET_LASTMOD_BY_ID =
|
|
||||||
"select lastModifiedTimestamp " +
|
|
||||||
" from "+TABLE_NAME+" " +
|
|
||||||
" where customerId = ? and " + COL_ID + " = ?";
|
|
||||||
|
|
||||||
private static final String SQL_INSERT =
|
|
||||||
"insert into "+TABLE_NAME+" ( "
|
|
||||||
+ ALL_COLUMNS_FOR_INSERT
|
|
||||||
+ " ) values ( "+BIND_VARS_FOR_INSERT+" ) ";
|
|
||||||
|
|
||||||
private static final String SQL_DELETE =
|
|
||||||
"delete from "+TABLE_NAME+" where customerId = ? and " + COL_ID + " = ? ";
|
|
||||||
|
|
||||||
private static final String SQL_UPDATE =
|
|
||||||
"update "+TABLE_NAME+" set "
|
|
||||||
+ ALL_COLUMNS_UPDATE +
|
|
||||||
" where customerId = ? and " + COL_ID + " = ? "
|
|
||||||
+ " and ( lastModifiedTimestamp = ? or ? = true) " //last parameter will allow us to skip check for concurrent modification, if necessary
|
|
||||||
;
|
|
||||||
|
|
||||||
private static final String SQL_GET_ALL_IN_SET = "select " + ALL_COLUMNS + " from "+TABLE_NAME + " where customerId = ? and "+ COL_ID +" in ";
|
|
||||||
|
|
||||||
private static final String SQL_APPEND_SEARCH_MAC_SUBSTRING =
|
|
||||||
"and macAddressString like ? ";
|
|
||||||
|
|
||||||
private static final String SQL_PAGING_SUFFIX = " LIMIT ? OFFSET ? ";
|
|
||||||
private static final String SORT_SUFFIX = "";
|
|
||||||
|
|
||||||
private static final String SQL_INSERT_BLOCK_LIST = "insert into client_blocklist (customerId, macAddress) values (?, ?) ";
|
|
||||||
private static final String SQL_DELETE_BLOCK_LIST = "delete from client_blocklist where customerId = ? and macAddress = ? ";
|
|
||||||
|
|
||||||
|
|
||||||
private static final RowMapper<Client> clientRowMapper = new ClientRowMapper();
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired(required=false)
|
|
||||||
public void setDataSource(ClientDataSourceInterface dataSource) {
|
|
||||||
setDataSource((DataSource)dataSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Client create(final Client client) {
|
|
||||||
|
|
||||||
final long ts = System.currentTimeMillis();
|
|
||||||
|
|
||||||
try{
|
|
||||||
jdbcTemplate.update(
|
|
||||||
new PreparedStatementCreator() {
|
|
||||||
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
|
|
||||||
PreparedStatement ps = connection.prepareStatement(SQL_INSERT );
|
|
||||||
int colIdx = 1;
|
|
||||||
|
|
||||||
//TODO: add remaining properties from Client here
|
|
||||||
ps.setLong(colIdx++, client.getMacAddress().getAddressAsLong());
|
|
||||||
ps.setString(colIdx++, client.getMacAddress().getAddressAsString());
|
|
||||||
ps.setInt(colIdx++, client.getCustomerId());
|
|
||||||
ps.setBytes(colIdx++, (client.getDetails()!=null)?client.getDetails().toZippedBytes():null);
|
|
||||||
|
|
||||||
ps.setLong(colIdx++, ts);
|
|
||||||
ps.setLong(colIdx++, ts);
|
|
||||||
|
|
||||||
return ps;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}catch (DuplicateKeyException e) {
|
|
||||||
throw new DsDuplicateEntityException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
//update blocked_client table, if needed
|
|
||||||
if((client.getDetails() instanceof ClientInfoDetails)
|
|
||||||
&& ((ClientInfoDetails)client.getDetails()).getBlocklistDetails()!=null
|
|
||||||
&& ((ClientInfoDetails)client.getDetails()).getBlocklistDetails().isEnabled()
|
|
||||||
) {
|
|
||||||
|
|
||||||
this.jdbcTemplate.update( SQL_INSERT_BLOCK_LIST,
|
|
||||||
client.getCustomerId(), client.getMacAddress().getAddressAsLong());
|
|
||||||
|
|
||||||
client.setNeedToUpdateBlocklist(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
client.setCreatedTimestamp(ts);
|
|
||||||
client.setLastModifiedTimestamp(ts);
|
|
||||||
|
|
||||||
|
|
||||||
LOG.debug("Stored Client {}", client);
|
|
||||||
|
|
||||||
return client.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Transactional(noRollbackFor = { EmptyResultDataAccessException.class })
|
|
||||||
public Client getOrNull(int customerId, MacAddress clientMac) {
|
|
||||||
LOG.debug("Looking up Client for id {} {}", customerId, clientMac);
|
|
||||||
|
|
||||||
try{
|
|
||||||
Client client = this.jdbcTemplate.queryForObject(
|
|
||||||
SQL_GET_BY_ID,
|
|
||||||
clientRowMapper, customerId, clientMac.getAddressAsLong());
|
|
||||||
|
|
||||||
LOG.debug("Found Client {}", client);
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}catch (EmptyResultDataAccessException e) {
|
|
||||||
LOG.debug("Could not find Client for id {} {}", customerId, clientMac);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Client update(Client client) {
|
|
||||||
|
|
||||||
long newLastModifiedTs = System.currentTimeMillis();
|
|
||||||
long incomingLastModifiedTs = client.getLastModifiedTimestamp();
|
|
||||||
|
|
||||||
Client existingClient = getOrNull(client.getCustomerId(), client.getMacAddress());
|
|
||||||
|
|
||||||
if(existingClient==null) {
|
|
||||||
LOG.debug("Cannot find Client for {} {}", client.getCustomerId(), client.getMacAddress());
|
|
||||||
throw new DsEntityNotFoundException("Client not found " + client.getCustomerId() + " " + client.getMacAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
int updateCount = this.jdbcTemplate.update(SQL_UPDATE, new Object[]{
|
|
||||||
//TODO: add remaining properties from Client here
|
|
||||||
(client.getDetails()!=null)?client.getDetails().toZippedBytes():null ,
|
|
||||||
|
|
||||||
//client.getCreatedTimestamp(), - not updating this one
|
|
||||||
newLastModifiedTs,
|
|
||||||
|
|
||||||
// use id for update operation
|
|
||||||
client.getCustomerId(),
|
|
||||||
client.getMacAddress().getAddressAsLong(),
|
|
||||||
// use lastModifiedTimestamp for data protection against concurrent modifications
|
|
||||||
incomingLastModifiedTs,
|
|
||||||
isSkipCheckForConcurrentUpdates()
|
|
||||||
});
|
|
||||||
|
|
||||||
if(updateCount==0){
|
|
||||||
|
|
||||||
if(isSkipCheckForConcurrentUpdates()){
|
|
||||||
//in this case we did not request protection against concurrent updates,
|
|
||||||
//so the updateCount is 0 because record in db was not found
|
|
||||||
throw new EmptyResultDataAccessException(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
long recordTimestamp = existingClient.getLastModifiedTimestamp();
|
|
||||||
|
|
||||||
LOG.debug("Concurrent modification detected for Client with id {} {} expected version is {} but version in db was {}",
|
|
||||||
client.getCustomerId(),
|
|
||||||
client.getMacAddress().getAddressAsLong(),
|
|
||||||
incomingLastModifiedTs,
|
|
||||||
recordTimestamp
|
|
||||||
);
|
|
||||||
throw new DsConcurrentModificationException("Concurrent modification detected for Client with id "
|
|
||||||
+ client.getCustomerId() + " " + client.getMacAddress()
|
|
||||||
+" expected version is " + incomingLastModifiedTs
|
|
||||||
+" but version in db was " + recordTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//update client_blocklist table, if the blocking state of the client has changed
|
|
||||||
boolean existingClientBlocked = (existingClient.getDetails() instanceof ClientInfoDetails)
|
|
||||||
&& ((ClientInfoDetails)existingClient.getDetails()).getBlocklistDetails()!=null
|
|
||||||
&& ((ClientInfoDetails)existingClient.getDetails()).getBlocklistDetails().isEnabled();
|
|
||||||
|
|
||||||
boolean updatedClientBlocked = (client.getDetails() instanceof ClientInfoDetails)
|
|
||||||
&& ((ClientInfoDetails)client.getDetails()).getBlocklistDetails()!=null
|
|
||||||
&& ((ClientInfoDetails)client.getDetails()).getBlocklistDetails().isEnabled();
|
|
||||||
|
|
||||||
if(existingClientBlocked != updatedClientBlocked) {
|
|
||||||
if(updatedClientBlocked) {
|
|
||||||
//insert record into client_blocklist table
|
|
||||||
this.jdbcTemplate.update( SQL_INSERT_BLOCK_LIST,
|
|
||||||
client.getCustomerId(), client.getMacAddress().getAddressAsLong());
|
|
||||||
} else {
|
|
||||||
//delete record from client_blocklist table
|
|
||||||
this.jdbcTemplate.update( SQL_DELETE_BLOCK_LIST,
|
|
||||||
client.getCustomerId(), client.getMacAddress().getAddressAsLong());
|
|
||||||
}
|
|
||||||
|
|
||||||
//notify the caller that block list needs to be updated
|
|
||||||
client.setNeedToUpdateBlocklist(true);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//make a copy so that we don't accidentally update caller's version by reference
|
|
||||||
Client clientCopy = client.clone();
|
|
||||||
clientCopy.setLastModifiedTimestamp(newLastModifiedTs);
|
|
||||||
|
|
||||||
LOG.debug("Updated Client {}", clientCopy);
|
|
||||||
|
|
||||||
return clientCopy;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Client delete(int customerId, MacAddress clientMac) {
|
|
||||||
Client client = getOrNull(customerId, clientMac);
|
|
||||||
if(client!=null) {
|
|
||||||
this.jdbcTemplate.update(SQL_DELETE, customerId, clientMac.getAddressAsLong());
|
|
||||||
} else {
|
|
||||||
throw new DsEntityNotFoundException("Cannot find Client for id " + customerId + " " + clientMac);
|
|
||||||
}
|
|
||||||
|
|
||||||
//delete from client_blocklist table happens by foreign key cascade
|
|
||||||
//but we still need to tell the caller if the blocklist need to be updated
|
|
||||||
if((client.getDetails() instanceof ClientInfoDetails)
|
|
||||||
&& ((ClientInfoDetails)client.getDetails()).getBlocklistDetails()!=null
|
|
||||||
&& ((ClientInfoDetails)client.getDetails()).getBlocklistDetails().isEnabled()
|
|
||||||
) {
|
|
||||||
client.setNeedToUpdateBlocklist(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.debug("Deleted Client {} {}", customerId, clientMac);
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Client> getAllForCustomer(int customerId) {
|
|
||||||
LOG.debug("Looking up Clients for customer {}", customerId);
|
|
||||||
|
|
||||||
List<Client> ret = this.jdbcTemplate.query(SQL_GET_BY_CUSTOMER_ID,
|
|
||||||
clientRowMapper, customerId);
|
|
||||||
|
|
||||||
LOG.debug("Found Clients for customer {} : {}", customerId, ret);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Client> get(int customerId, Set<MacAddress> clientMacSet) {
|
|
||||||
LOG.debug("calling get({}, {})", customerId, clientMacSet);
|
|
||||||
|
|
||||||
if (clientMacSet == null || clientMacSet.isEmpty()) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder set = new StringBuilder(256);
|
|
||||||
set.append("(");
|
|
||||||
for(int i =0; i< clientMacSet.size(); i++) {
|
|
||||||
set.append("?,");
|
|
||||||
}
|
|
||||||
//remove last comma
|
|
||||||
set.deleteCharAt(set.length()-1);
|
|
||||||
set.append(")");
|
|
||||||
|
|
||||||
String query = SQL_GET_ALL_IN_SET + set;
|
|
||||||
ArrayList<Object> bindVars = new ArrayList<>();
|
|
||||||
bindVars.add(customerId);
|
|
||||||
clientMacSet.forEach(m -> bindVars.add(m.getAddressAsLong()) );
|
|
||||||
|
|
||||||
List<Client> results = this.jdbcTemplate.query(query, bindVars.toArray(), clientRowMapper);
|
|
||||||
|
|
||||||
LOG.debug("get({}, {}) returns {} record(s)", customerId, clientMacSet, results.size());
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PaginationResponse<Client> getForCustomer(int customerId, String macSubstring,
|
|
||||||
List<ColumnAndSort> sortBy, PaginationContext<Client> context) {
|
|
||||||
|
|
||||||
PaginationResponse<Client> ret = new PaginationResponse<>();
|
|
||||||
ret.setContext(context.clone());
|
|
||||||
|
|
||||||
if (ret.getContext().isLastPage()) {
|
|
||||||
// no more pages available according to the context
|
|
||||||
LOG.debug(
|
|
||||||
"No more pages available when looking up Clients for customer {} macSubstring {} with last returned page number {}",
|
|
||||||
customerId, macSubstring, context.getLastReturnedPageNumber());
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.debug("Looking up Clients for customer {} macSubstring {} with last returned page number {}",
|
|
||||||
customerId, macSubstring, context.getLastReturnedPageNumber());
|
|
||||||
|
|
||||||
String query = SQL_GET_BY_CUSTOMER_ID;
|
|
||||||
|
|
||||||
// add filters for the query
|
|
||||||
ArrayList<Object> queryArgs = new ArrayList<>();
|
|
||||||
queryArgs.add(customerId);
|
|
||||||
|
|
||||||
if (macSubstring != null) {
|
|
||||||
query += SQL_APPEND_SEARCH_MAC_SUBSTRING;
|
|
||||||
|
|
||||||
queryArgs.add("%" + macSubstring.toLowerCase() + "%");
|
|
||||||
}
|
|
||||||
|
|
||||||
// add sorting options for the query
|
|
||||||
StringBuilder strbSort = new StringBuilder(100);
|
|
||||||
strbSort.append(" order by ");
|
|
||||||
|
|
||||||
if (sortBy != null && !sortBy.isEmpty()) {
|
|
||||||
|
|
||||||
// use supplied sorting options
|
|
||||||
for (ColumnAndSort column : sortBy) {
|
|
||||||
if (!ALL_COLUMNS_LOWERCASE.contains(column.getColumnName().toLowerCase())) {
|
|
||||||
// unknown column, skip it
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
strbSort.append(column.getColumnName());
|
|
||||||
|
|
||||||
if (column.getSortOrder() == SortOrder.desc) {
|
|
||||||
strbSort.append(" desc");
|
|
||||||
}
|
|
||||||
|
|
||||||
strbSort.append(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove last ','
|
|
||||||
strbSort.deleteCharAt(strbSort.length() - 1);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// no sort order was specified - sort by id to have consistent
|
|
||||||
// paging
|
|
||||||
strbSort.append(COL_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
query += strbSort.toString();
|
|
||||||
|
|
||||||
// add pagination parameters for the query
|
|
||||||
query += SQL_PAGING_SUFFIX ;
|
|
||||||
|
|
||||||
queryArgs.add(context.getMaxItemsPerPage());
|
|
||||||
queryArgs.add(context.getTotalItemsReturned());
|
|
||||||
|
|
||||||
/*
|
|
||||||
* https://www.citusdata.com/blog/2016/03/30/five-ways-to-paginate/
|
|
||||||
* Choosing offset=1000 makes cost about 19 and has a 0.609 ms execution
|
|
||||||
* time. Once offset=5,000,000 the cost goes up to 92734 and execution
|
|
||||||
* time is 758.484 ms. - DT: still acceptable for our use case
|
|
||||||
*/
|
|
||||||
List<Client> pageItems = this.jdbcTemplate.query(query, queryArgs.toArray(),
|
|
||||||
clientRowMapper);
|
|
||||||
|
|
||||||
LOG.debug("Found {} Clients for customer {} macSubstring {} with last returned page number {}",
|
|
||||||
pageItems.size(), customerId, macSubstring, context.getLastReturnedPageNumber());
|
|
||||||
|
|
||||||
ret.setItems(pageItems);
|
|
||||||
|
|
||||||
// adjust context for the next page
|
|
||||||
ret.prepareForNextPage();
|
|
||||||
|
|
||||||
// startAfterItem is not used in RDBMS datastores, set it to null
|
|
||||||
ret.getContext().setStartAfterItem(null);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public List<Client> getBlockedClients(int customerId) {
|
|
||||||
LOG.debug("calling getBlockedClients({})", customerId);
|
|
||||||
|
|
||||||
List<Client> results = this.jdbcTemplate.query(SQL_GET_BLOCKED_CLIENTS, clientRowMapper, customerId);
|
|
||||||
|
|
||||||
LOG.debug("getBlockedClients({}) returns {} record(s)", customerId, results.size());
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package com.telecominfraproject.wlan.client.datastore.rdbms;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Profile;
|
|
||||||
import org.springframework.context.annotation.PropertySource;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.core.server.jdbc.BaseDataSourceConfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author dtoptygin
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@Profile("!use_single_ds")
|
|
||||||
@PropertySource({ "${client-ds.props:classpath:client-ds.properties}" })
|
|
||||||
public class ClientDataSourceConfig extends BaseDataSourceConfig {
|
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ClientDataSourceInterface clientDataSourceInterface(){
|
|
||||||
|
|
||||||
ClientDataSourceInterface ret = new ClientDataSourceImpl(getDataSource(), getKeyColumnConverter());
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDataSourceName() {
|
|
||||||
return "client-ds";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package com.telecominfraproject.wlan.client.datastore.rdbms;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.core.server.jdbc.BaseJDbcDataSource;
|
|
||||||
import com.telecominfraproject.wlan.core.server.jdbc.BaseKeyColumnConverter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author dtoptygin
|
|
||||||
* SQL DataSource that is used by Client Service
|
|
||||||
*/
|
|
||||||
public class ClientDataSourceImpl extends BaseJDbcDataSource implements ClientDataSourceInterface {
|
|
||||||
|
|
||||||
public ClientDataSourceImpl(DataSource targetDataSource, BaseKeyColumnConverter keyColumnConverter){
|
|
||||||
super(targetDataSource, keyColumnConverter);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.telecominfraproject.wlan.client.datastore.rdbms;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author dtoptygin
|
|
||||||
* Marker interface to distinguish SQL DataSource that is used by Client Service
|
|
||||||
*/
|
|
||||||
public interface ClientDataSourceInterface extends DataSource {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
package com.telecominfraproject.wlan.client.datastore.rdbms;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.core.model.equipment.MacAddress;
|
|
||||||
import com.telecominfraproject.wlan.core.model.pagination.ColumnAndSort;
|
|
||||||
import com.telecominfraproject.wlan.core.model.pagination.PaginationContext;
|
|
||||||
import com.telecominfraproject.wlan.core.model.pagination.PaginationResponse;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.client.datastore.ClientDatastore;
|
|
||||||
import com.telecominfraproject.wlan.client.models.Client;
|
|
||||||
import com.telecominfraproject.wlan.client.session.models.ClientSession;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author dtoptygin
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class ClientDatastoreRdbms implements ClientDatastore {
|
|
||||||
|
|
||||||
@Autowired ClientDAO clientDAO;
|
|
||||||
@Autowired ClientSessionDAO clientSessionDAO;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Client create(Client client) {
|
|
||||||
return clientDAO.create(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Client getOrNull(int customerId, MacAddress clientMac) {
|
|
||||||
return clientDAO.getOrNull(customerId, clientMac);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Client update(Client client) {
|
|
||||||
return clientDAO.update(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Client delete(int customerId, MacAddress clientMac) {
|
|
||||||
return clientDAO.delete(customerId, clientMac);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Client> get(int customerId, Set<MacAddress> clientMacSet) {
|
|
||||||
return clientDAO.get(customerId, clientMacSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Client> getBlockedClients(int customerId) {
|
|
||||||
return clientDAO.getBlockedClients(customerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PaginationResponse<Client> getForCustomer(int customerId, String macSubstring,
|
|
||||||
List<ColumnAndSort> sortBy, PaginationContext<Client> context) {
|
|
||||||
|
|
||||||
if(context == null) {
|
|
||||||
context = new PaginationContext<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return clientDAO.getForCustomer(customerId, macSubstring, sortBy, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClientSession getSessionOrNull(int customerId, long equipmentId, MacAddress clientMac) {
|
|
||||||
return clientSessionDAO.getSessionOrNull(customerId, equipmentId, clientMac);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClientSession updateSession(ClientSession clientSession) {
|
|
||||||
return clientSessionDAO.updateSession(clientSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClientSession deleteSession(int customerId, long equipmentId, MacAddress clientMac) {
|
|
||||||
return clientSessionDAO.deleteSession(customerId, equipmentId, clientMac);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<ClientSession> getSessions(int customerId, Set<MacAddress> clientMacSet) {
|
|
||||||
return clientSessionDAO.getSessions(customerId, clientMacSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PaginationResponse<ClientSession> getSessionsForCustomer(int customerId, Set<Long> equipmentIds, Set<Long> locationIds,
|
|
||||||
String macSubstring, List<ColumnAndSort> sortBy, PaginationContext<ClientSession> context) {
|
|
||||||
|
|
||||||
if(context == null) {
|
|
||||||
context = new PaginationContext<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return clientSessionDAO.getSessionsForCustomer(customerId, equipmentIds, locationIds, macSubstring, sortBy, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package com.telecominfraproject.wlan.client.datastore.rdbms;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.core.model.equipment.MacAddress;
|
|
||||||
import com.telecominfraproject.wlan.core.model.json.BaseJsonModel;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.client.models.Client;
|
|
||||||
import com.telecominfraproject.wlan.client.models.ClientDetails;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author dtoptygin
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class ClientRowMapper implements RowMapper<Client> {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ClientRowMapper.class);
|
|
||||||
|
|
||||||
public Client mapRow(ResultSet rs, int rowNum) throws SQLException {
|
|
||||||
Client client = new Client();
|
|
||||||
int colIdx=1;
|
|
||||||
client.setMacAddress(new MacAddress(rs.getLong(colIdx++)));
|
|
||||||
// macAddressString here does not need to map again to Client Object
|
|
||||||
colIdx++;
|
|
||||||
|
|
||||||
//TODO: add columns from properties Client in here.
|
|
||||||
//make sure order of fields is the same as defined in Client
|
|
||||||
client.setCustomerId(rs.getInt(colIdx++));
|
|
||||||
|
|
||||||
byte[] zippedBytes = rs.getBytes(colIdx++);
|
|
||||||
if (zippedBytes !=null) {
|
|
||||||
try {
|
|
||||||
ClientDetails details = BaseJsonModel.fromZippedBytes(zippedBytes, ClientDetails.class);
|
|
||||||
client.setDetails(details);
|
|
||||||
} catch (RuntimeException exp) {
|
|
||||||
LOG.error("Failed to decode ClientDetails from database for id {} {}", client.getCustomerId(), client.getMacAddress());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client.setCreatedTimestamp(rs.getLong(colIdx++));
|
|
||||||
client.setLastModifiedTimestamp(rs.getLong(colIdx++));
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,470 +0,0 @@
|
|||||||
package com.telecominfraproject.wlan.client.datastore.rdbms;
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.dao.DuplicateKeyException;
|
|
||||||
import org.springframework.dao.EmptyResultDataAccessException;
|
|
||||||
import org.springframework.jdbc.core.PreparedStatementCreator;
|
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.client.session.models.ClientSession;
|
|
||||||
import com.telecominfraproject.wlan.core.model.equipment.MacAddress;
|
|
||||||
import com.telecominfraproject.wlan.core.model.pagination.ColumnAndSort;
|
|
||||||
import com.telecominfraproject.wlan.core.model.pagination.PaginationContext;
|
|
||||||
import com.telecominfraproject.wlan.core.model.pagination.PaginationResponse;
|
|
||||||
import com.telecominfraproject.wlan.core.model.pagination.SortOrder;
|
|
||||||
import com.telecominfraproject.wlan.core.server.jdbc.BaseJdbcDao;
|
|
||||||
import com.telecominfraproject.wlan.datastore.exceptions.DsConcurrentModificationException;
|
|
||||||
import com.telecominfraproject.wlan.datastore.exceptions.DsDuplicateEntityException;
|
|
||||||
import com.telecominfraproject.wlan.datastore.exceptions.DsEntityNotFoundException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author dtoptygin
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Repository
|
|
||||||
@Transactional(propagation = Propagation.MANDATORY)
|
|
||||||
public class ClientSessionDAO extends BaseJdbcDao {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ClientDatastoreRdbms.class);
|
|
||||||
|
|
||||||
private static final String COL_ID = "macAddress";
|
|
||||||
|
|
||||||
private static final String[] GENERATED_KEY_COLS = { };
|
|
||||||
|
|
||||||
private static final String[] ALL_COLUMNS_LIST = {
|
|
||||||
COL_ID,
|
|
||||||
"macAddressString",
|
|
||||||
|
|
||||||
//TODO: add colums from properties ClientSession in here
|
|
||||||
"customerId",
|
|
||||||
"equipmentId",
|
|
||||||
"locationId",
|
|
||||||
"details",
|
|
||||||
//make sure the order of properties matches this list and list in ClientSessionRowMapper and list in create/update methods
|
|
||||||
|
|
||||||
"lastModifiedTimestamp"
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final Set<String> columnsToSkipForInsert = new HashSet<>(Arrays.asList());
|
|
||||||
private static final Set<String> columnsToSkipForUpdate = new HashSet<>(Arrays.asList(COL_ID, "macAddressString", "customerId", "equipmentId"));
|
|
||||||
|
|
||||||
private static final String TABLE_NAME = "client_session";
|
|
||||||
private static final String TABLE_PREFIX = "s.";
|
|
||||||
private static final String ALL_COLUMNS;
|
|
||||||
|
|
||||||
private static final Set<String> ALL_COLUMNS_LOWERCASE = new HashSet<>();
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
//use this for queries where multiple tables are involved
|
|
||||||
private static final String ALL_COLUMNS_WITH_PREFIX;
|
|
||||||
|
|
||||||
private static final String ALL_COLUMNS_FOR_INSERT;
|
|
||||||
private static final String BIND_VARS_FOR_INSERT;
|
|
||||||
private static final String ALL_COLUMNS_UPDATE;
|
|
||||||
|
|
||||||
static{
|
|
||||||
StringBuilder strbAllColumns = new StringBuilder(1024);
|
|
||||||
StringBuilder strbAllColumnsWithPrefix = new StringBuilder(1024);
|
|
||||||
StringBuilder strbAllColumnsForInsert = new StringBuilder(1024);
|
|
||||||
StringBuilder strbBindVarsForInsert = new StringBuilder(128);
|
|
||||||
StringBuilder strbColumnsForUpdate = new StringBuilder(512);
|
|
||||||
for(String colName: ALL_COLUMNS_LIST){
|
|
||||||
|
|
||||||
ALL_COLUMNS_LOWERCASE.add(colName.toLowerCase());
|
|
||||||
|
|
||||||
strbAllColumns.append(colName).append(",");
|
|
||||||
strbAllColumnsWithPrefix.append(TABLE_PREFIX).append(colName).append(",");
|
|
||||||
|
|
||||||
if(!columnsToSkipForInsert.contains(colName)){
|
|
||||||
strbAllColumnsForInsert.append(colName).append(",");
|
|
||||||
strbBindVarsForInsert.append("?,");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!columnsToSkipForUpdate.contains(colName)){
|
|
||||||
strbColumnsForUpdate.append(colName).append("=?,");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove trailing ','
|
|
||||||
strbAllColumns.deleteCharAt(strbAllColumns.length() - 1);
|
|
||||||
strbAllColumnsWithPrefix.deleteCharAt(strbAllColumnsWithPrefix.length() - 1);
|
|
||||||
strbAllColumnsForInsert.deleteCharAt(strbAllColumnsForInsert.length() - 1);
|
|
||||||
strbBindVarsForInsert.deleteCharAt(strbBindVarsForInsert.length() - 1);
|
|
||||||
strbColumnsForUpdate.deleteCharAt(strbColumnsForUpdate.length() - 1);
|
|
||||||
|
|
||||||
ALL_COLUMNS = strbAllColumns.toString();
|
|
||||||
ALL_COLUMNS_WITH_PREFIX = strbAllColumnsWithPrefix.toString();
|
|
||||||
ALL_COLUMNS_FOR_INSERT = strbAllColumnsForInsert.toString();
|
|
||||||
BIND_VARS_FOR_INSERT = strbBindVarsForInsert.toString();
|
|
||||||
ALL_COLUMNS_UPDATE = strbColumnsForUpdate.toString();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static final String SQL_GET_BY_ID =
|
|
||||||
"select " + ALL_COLUMNS +
|
|
||||||
" from "+TABLE_NAME+" " +
|
|
||||||
" where customerId = ? and equipmentId = ? and " + COL_ID + " = ?";
|
|
||||||
|
|
||||||
private static final String SQL_GET_BY_CUSTOMER_ID =
|
|
||||||
"select " + ALL_COLUMNS +
|
|
||||||
" from " + TABLE_NAME + " " +
|
|
||||||
" where customerId = ? ";
|
|
||||||
|
|
||||||
private static final String SQL_GET_LASTMOD_BY_ID =
|
|
||||||
"select lastModifiedTimestamp " +
|
|
||||||
" from "+TABLE_NAME+" " +
|
|
||||||
" where customerId = ? and equipmentId = ? and " + COL_ID + " = ?";
|
|
||||||
|
|
||||||
private static final String SQL_INSERT =
|
|
||||||
"insert into "+TABLE_NAME+" ( "
|
|
||||||
+ ALL_COLUMNS_FOR_INSERT
|
|
||||||
+ " ) values ( "+BIND_VARS_FOR_INSERT+" ) ";
|
|
||||||
|
|
||||||
private static final String SQL_DELETE =
|
|
||||||
"delete from "+TABLE_NAME+" where customerId = ? and equipmentId = ? and " + COL_ID + " = ? ";
|
|
||||||
|
|
||||||
private static final String SQL_UPDATE =
|
|
||||||
"update "+TABLE_NAME+" set "
|
|
||||||
+ ALL_COLUMNS_UPDATE +
|
|
||||||
" where customerId = ? and equipmentId = ? and " + COL_ID + " = ? "
|
|
||||||
+ " and ( lastModifiedTimestamp = ? or ? = true) " //last parameter will allow us to skip check for concurrent modification, if necessary
|
|
||||||
;
|
|
||||||
|
|
||||||
private static final String SQL_GET_ALL_IN_SET = "select " + ALL_COLUMNS + " from "+TABLE_NAME + " where customerId = ? and "+ COL_ID +" in ";
|
|
||||||
|
|
||||||
private static final String SQL_APPEND_SEARCH_MAC_SUBSTRING =
|
|
||||||
"and macAddressString like ? ";
|
|
||||||
|
|
||||||
private static final String SQL_PAGING_SUFFIX = " LIMIT ? OFFSET ? ";
|
|
||||||
private static final String SORT_SUFFIX = "";
|
|
||||||
|
|
||||||
|
|
||||||
private static final RowMapper<ClientSession> clientSessionRowMapper = new ClientSessionRowMapper();
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired(required=false)
|
|
||||||
public void setDataSource(ClientDataSourceInterface dataSource) {
|
|
||||||
setDataSource((DataSource)dataSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
void afterPropertiesSet(){
|
|
||||||
setSkipCheckForConcurrentUpdates(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientSession create(final ClientSession clientSession) {
|
|
||||||
|
|
||||||
final long ts = System.currentTimeMillis();
|
|
||||||
|
|
||||||
try{
|
|
||||||
jdbcTemplate.update(
|
|
||||||
new PreparedStatementCreator() {
|
|
||||||
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
|
|
||||||
PreparedStatement ps = connection.prepareStatement(SQL_INSERT );
|
|
||||||
int colIdx = 1;
|
|
||||||
|
|
||||||
//TODO: add remaining properties from Client here
|
|
||||||
ps.setLong(colIdx++, clientSession.getMacAddress().getAddressAsLong());
|
|
||||||
ps.setString(colIdx++, clientSession.getMacAddress().getAddressAsString());
|
|
||||||
ps.setInt(colIdx++, clientSession.getCustomerId());
|
|
||||||
ps.setLong(colIdx++, clientSession.getEquipmentId());
|
|
||||||
ps.setLong(colIdx++, clientSession.getLocationId());
|
|
||||||
ps.setBytes(colIdx++, (clientSession.getDetails()!=null)?clientSession.getDetails().toZippedBytes():null);
|
|
||||||
|
|
||||||
ps.setLong(colIdx++, ts);
|
|
||||||
|
|
||||||
return ps;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}catch (DuplicateKeyException e) {
|
|
||||||
throw new DsDuplicateEntityException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
clientSession.setLastModifiedTimestamp(ts);
|
|
||||||
|
|
||||||
|
|
||||||
LOG.debug("Stored Client session {}", clientSession);
|
|
||||||
|
|
||||||
return clientSession.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Transactional(noRollbackFor = { EmptyResultDataAccessException.class })
|
|
||||||
public ClientSession getSessionOrNull(int customerId, long equipmentId, MacAddress clientMac) {
|
|
||||||
LOG.debug("Looking up Client session for id {} {} {}", customerId, equipmentId, clientMac);
|
|
||||||
|
|
||||||
try{
|
|
||||||
ClientSession client = this.jdbcTemplate.queryForObject(
|
|
||||||
SQL_GET_BY_ID,
|
|
||||||
clientSessionRowMapper, customerId, equipmentId, clientMac.getAddressAsLong());
|
|
||||||
|
|
||||||
LOG.debug("Found Client session {}", client);
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}catch (EmptyResultDataAccessException e) {
|
|
||||||
LOG.debug("Could not find Client for id {} {}", customerId, clientMac);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientSession updateSession(ClientSession clientSession) {
|
|
||||||
|
|
||||||
long newLastModifiedTs = System.currentTimeMillis();
|
|
||||||
long incomingLastModifiedTs = clientSession.getLastModifiedTimestamp();
|
|
||||||
|
|
||||||
int updateCount = this.jdbcTemplate.update(SQL_UPDATE, new Object[]{
|
|
||||||
//TODO: add remaining properties from Client session here
|
|
||||||
clientSession.getLocationId(),
|
|
||||||
(clientSession.getDetails()!=null)?clientSession.getDetails().toZippedBytes():null ,
|
|
||||||
newLastModifiedTs,
|
|
||||||
|
|
||||||
// use id for update operation
|
|
||||||
clientSession.getCustomerId(),
|
|
||||||
clientSession.getEquipmentId(),
|
|
||||||
clientSession.getMacAddress().getAddressAsLong(),
|
|
||||||
// use lastModifiedTimestamp for data protection against concurrent modifications
|
|
||||||
incomingLastModifiedTs,
|
|
||||||
isSkipCheckForConcurrentUpdates()
|
|
||||||
});
|
|
||||||
|
|
||||||
if(updateCount==0){
|
|
||||||
|
|
||||||
try{
|
|
||||||
|
|
||||||
//find out if record could not be updated because it does not exist or because it was modified concurrently
|
|
||||||
long recordTimestamp = this.jdbcTemplate.queryForObject(
|
|
||||||
SQL_GET_LASTMOD_BY_ID,
|
|
||||||
Long.class,
|
|
||||||
clientSession.getCustomerId(), clientSession.getEquipmentId(), clientSession.getMacAddress().getAddressAsLong()
|
|
||||||
);
|
|
||||||
|
|
||||||
if(!isSkipCheckForConcurrentUpdates()){
|
|
||||||
LOG.debug("Concurrent modification detected for Client session with id {}:{}:{} expected version is {} but version in db was {}",
|
|
||||||
clientSession.getCustomerId(), clientSession.getEquipmentId(), clientSession.getMacAddress(),
|
|
||||||
incomingLastModifiedTs,
|
|
||||||
recordTimestamp
|
|
||||||
);
|
|
||||||
throw new DsConcurrentModificationException("Concurrent modification detected for Client session with id "
|
|
||||||
+ clientSession.getCustomerId() + ":" + clientSession.getEquipmentId() + ":" + clientSession.getMacAddress()
|
|
||||||
+" expected version is " + incomingLastModifiedTs
|
|
||||||
+" but version in db was " + recordTimestamp
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}catch (EmptyResultDataAccessException e) {
|
|
||||||
LOG.debug("Could not find Client for id {}:{}:{}", clientSession.getCustomerId(), clientSession.getEquipmentId(), clientSession.getMacAddress());
|
|
||||||
|
|
||||||
if(isSkipCheckForConcurrentUpdates()){
|
|
||||||
//in this case we did not request protection against concurrent updates,
|
|
||||||
//so the updateCount is 0 because record in db was not found
|
|
||||||
//we'll create a new record
|
|
||||||
return create(clientSession);
|
|
||||||
} else {
|
|
||||||
throw new DsEntityNotFoundException("Client session not found " + clientSession.getCustomerId() + ":"
|
|
||||||
+ clientSession.getEquipmentId() + ":" + clientSession.getMacAddress());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//make a copy so that we don't accidentally update caller's version by reference
|
|
||||||
ClientSession clientSessionCopy = clientSession.clone();
|
|
||||||
clientSessionCopy.setLastModifiedTimestamp(newLastModifiedTs);
|
|
||||||
|
|
||||||
LOG.debug("Updated Client session {}", clientSessionCopy);
|
|
||||||
|
|
||||||
return clientSessionCopy;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public ClientSession deleteSession(int customerId, long equipmentId, MacAddress clientMac) {
|
|
||||||
ClientSession client = getSessionOrNull(customerId, equipmentId, clientMac);
|
|
||||||
if(client!=null) {
|
|
||||||
this.jdbcTemplate.update(SQL_DELETE, customerId, equipmentId, clientMac.getAddressAsLong());
|
|
||||||
} else {
|
|
||||||
throw new DsEntityNotFoundException("Cannot find Client for id " + customerId + " " + equipmentId+ " " + clientMac);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.debug("Deleted Client session {} {} {}", customerId, equipmentId, clientMac);
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ClientSession> getSessions(int customerId, Set<MacAddress> clientMacSet) {
|
|
||||||
LOG.debug("calling getSessions({}, {})", customerId, clientMacSet);
|
|
||||||
|
|
||||||
if (clientMacSet == null || clientMacSet.isEmpty()) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder set = new StringBuilder(256);
|
|
||||||
set.append("(");
|
|
||||||
for(int i =0; i< clientMacSet.size(); i++) {
|
|
||||||
set.append("?,");
|
|
||||||
}
|
|
||||||
//remove last comma
|
|
||||||
set.deleteCharAt(set.length()-1);
|
|
||||||
set.append(")");
|
|
||||||
|
|
||||||
String query = SQL_GET_ALL_IN_SET + set;
|
|
||||||
ArrayList<Object> bindVars = new ArrayList<>();
|
|
||||||
bindVars.add(customerId);
|
|
||||||
clientMacSet.forEach(m -> bindVars.add(m.getAddressAsLong()) );
|
|
||||||
|
|
||||||
List<ClientSession> results = this.jdbcTemplate.query(query, bindVars.toArray(), clientSessionRowMapper);
|
|
||||||
|
|
||||||
LOG.debug("getSessions({}, {}) returns {} record(s)", customerId, clientMacSet, results.size());
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public PaginationResponse<ClientSession> getSessionsForCustomer(int customerId, Set<Long> equipmentIds, Set<Long> locationIds,
|
|
||||||
String macSubstring, List<ColumnAndSort> sortBy, PaginationContext<ClientSession> context) {
|
|
||||||
|
|
||||||
PaginationResponse<ClientSession> ret = new PaginationResponse<>();
|
|
||||||
ret.setContext(context.clone());
|
|
||||||
|
|
||||||
if (ret.getContext().isLastPage()) {
|
|
||||||
// no more pages available according to the context
|
|
||||||
LOG.debug(
|
|
||||||
"No more pages available when looking up Client sessions for customer {} with last returned page number {}",
|
|
||||||
customerId, context.getLastReturnedPageNumber());
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.debug("Looking up Client sessions for customer {} with last returned page number {}",
|
|
||||||
customerId, context.getLastReturnedPageNumber());
|
|
||||||
|
|
||||||
String query = SQL_GET_BY_CUSTOMER_ID;
|
|
||||||
|
|
||||||
// add filters for the query
|
|
||||||
ArrayList<Object> queryArgs = new ArrayList<>();
|
|
||||||
queryArgs.add(customerId);
|
|
||||||
|
|
||||||
//add equipmentId filters
|
|
||||||
if (equipmentIds != null && !equipmentIds.isEmpty()) {
|
|
||||||
queryArgs.addAll(equipmentIds);
|
|
||||||
|
|
||||||
StringBuilder strb = new StringBuilder(100);
|
|
||||||
strb.append("and equipmentId in (");
|
|
||||||
for (int i = 0; i < equipmentIds.size(); i++) {
|
|
||||||
strb.append("?");
|
|
||||||
if (i < equipmentIds.size() - 1) {
|
|
||||||
strb.append(",");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
strb.append(") ");
|
|
||||||
|
|
||||||
query += strb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
//add locationId filters
|
|
||||||
if (locationIds != null && !locationIds.isEmpty()) {
|
|
||||||
queryArgs.addAll(locationIds);
|
|
||||||
|
|
||||||
StringBuilder strb = new StringBuilder(100);
|
|
||||||
strb.append("and locationId in (");
|
|
||||||
for (int i = 0; i < locationIds.size(); i++) {
|
|
||||||
strb.append("?");
|
|
||||||
if (i < locationIds.size() - 1) {
|
|
||||||
strb.append(",");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
strb.append(") ");
|
|
||||||
|
|
||||||
query += strb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
//add macSubstring filter
|
|
||||||
if (macSubstring != null && !macSubstring.isEmpty()) {
|
|
||||||
query += SQL_APPEND_SEARCH_MAC_SUBSTRING;
|
|
||||||
|
|
||||||
queryArgs.add("%" + macSubstring.toLowerCase() + "%");
|
|
||||||
}
|
|
||||||
|
|
||||||
// add sorting options for the query
|
|
||||||
StringBuilder strbSort = new StringBuilder(100);
|
|
||||||
strbSort.append(" order by ");
|
|
||||||
|
|
||||||
if (sortBy != null && !sortBy.isEmpty()) {
|
|
||||||
|
|
||||||
// use supplied sorting options
|
|
||||||
for (ColumnAndSort column : sortBy) {
|
|
||||||
if (!ALL_COLUMNS_LOWERCASE.contains(column.getColumnName().toLowerCase())) {
|
|
||||||
// unknown column, skip it
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
strbSort.append(column.getColumnName());
|
|
||||||
|
|
||||||
if (column.getSortOrder() == SortOrder.desc) {
|
|
||||||
strbSort.append(" desc");
|
|
||||||
}
|
|
||||||
|
|
||||||
strbSort.append(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove last ','
|
|
||||||
strbSort.deleteCharAt(strbSort.length() - 1);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// no sort order was specified - sort by id to have consistent
|
|
||||||
// paging
|
|
||||||
strbSort.append(COL_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
query += strbSort.toString();
|
|
||||||
|
|
||||||
// add pagination parameters for the query
|
|
||||||
query += SQL_PAGING_SUFFIX ;
|
|
||||||
|
|
||||||
queryArgs.add(context.getMaxItemsPerPage());
|
|
||||||
queryArgs.add(context.getTotalItemsReturned());
|
|
||||||
|
|
||||||
/*
|
|
||||||
* https://www.citusdata.com/blog/2016/03/30/five-ways-to-paginate/
|
|
||||||
* Choosing offset=1000 makes cost about 19 and has a 0.609 ms execution
|
|
||||||
* time. Once offset=5,000,000 the cost goes up to 92734 and execution
|
|
||||||
* time is 758.484 ms. - DT: still acceptable for our use case
|
|
||||||
*/
|
|
||||||
List<ClientSession> pageItems = this.jdbcTemplate.query(query, queryArgs.toArray(),
|
|
||||||
clientSessionRowMapper);
|
|
||||||
|
|
||||||
if (pageItems == null) {
|
|
||||||
LOG.debug("Cannot find Client sessions for customer {} with last returned page number {}",
|
|
||||||
customerId, context.getLastReturnedPageNumber());
|
|
||||||
} else {
|
|
||||||
LOG.debug("Found {} Client sessions for customer {} with last returned page number {}",
|
|
||||||
pageItems.size(), customerId, context.getLastReturnedPageNumber());
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.setItems(pageItems);
|
|
||||||
|
|
||||||
// adjust context for the next page
|
|
||||||
ret.prepareForNextPage();
|
|
||||||
|
|
||||||
// startAfterItem is not used in RDBMS datastores, set it to null
|
|
||||||
ret.getContext().setStartAfterItem(null);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package com.telecominfraproject.wlan.client.datastore.rdbms;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.core.model.equipment.MacAddress;
|
|
||||||
import com.telecominfraproject.wlan.core.model.json.BaseJsonModel;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.client.models.Client;
|
|
||||||
import com.telecominfraproject.wlan.client.models.ClientDetails;
|
|
||||||
import com.telecominfraproject.wlan.client.session.models.ClientSession;
|
|
||||||
import com.telecominfraproject.wlan.client.session.models.ClientSessionDetails;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author dtoptygin
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class ClientSessionRowMapper implements RowMapper<ClientSession> {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ClientSessionRowMapper.class);
|
|
||||||
|
|
||||||
public ClientSession mapRow(ResultSet rs, int rowNum) throws SQLException {
|
|
||||||
ClientSession clientSession = new ClientSession();
|
|
||||||
int colIdx=1;
|
|
||||||
clientSession.setMacAddress(new MacAddress(rs.getLong(colIdx++)));
|
|
||||||
// macAddressString here does not need to map again to ClientSession Object
|
|
||||||
colIdx++;
|
|
||||||
|
|
||||||
//TODO: add columns from properties ClientSession in here.
|
|
||||||
//make sure order of fields is the same as defined in ClientSession
|
|
||||||
clientSession.setCustomerId(rs.getInt(colIdx++));
|
|
||||||
clientSession.setEquipmentId(rs.getLong(colIdx++));
|
|
||||||
clientSession.setLocationId(rs.getLong(colIdx++));
|
|
||||||
|
|
||||||
byte[] zippedBytes = rs.getBytes(colIdx++);
|
|
||||||
if (zippedBytes !=null) {
|
|
||||||
try {
|
|
||||||
ClientSessionDetails details = BaseJsonModel.fromZippedBytes(zippedBytes, ClientSessionDetails.class);
|
|
||||||
clientSession.setDetails(details);
|
|
||||||
} catch (RuntimeException exp) {
|
|
||||||
LOG.error("Failed to decode ClientSessionDetails from database for id {} {} {}", clientSession.getCustomerId(), clientSession.getEquipmentId(), clientSession.getMacAddress());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clientSession.setLastModifiedTimestamp(rs.getLong(colIdx++));
|
|
||||||
|
|
||||||
return clientSession;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
create table if not exists client (
|
|
||||||
-- postgresql
|
|
||||||
macAddress bigint ,
|
|
||||||
macAddressString varchar(100) ,
|
|
||||||
|
|
||||||
customerId int,
|
|
||||||
details bytea,
|
|
||||||
|
|
||||||
createdTimestamp bigint not null,
|
|
||||||
lastModifiedTimestamp bigint not null,
|
|
||||||
|
|
||||||
primary key (customerId, macAddress)
|
|
||||||
);
|
|
||||||
|
|
||||||
alter table client add column if not exists macAddressString varchar(100);
|
|
||||||
|
|
||||||
create index if not exists idx_client_customerId on client (customerId);
|
|
||||||
create index if not exists idx_client_customerId_macAddressString on client (customerId, macAddressString);
|
|
||||||
|
|
||||||
create table if not exists client_blocklist (
|
|
||||||
-- postgresql
|
|
||||||
customerId int,
|
|
||||||
macAddress bigint ,
|
|
||||||
|
|
||||||
primary key (customerId, macAddress),
|
|
||||||
FOREIGN KEY (customerId, macAddress) REFERENCES client(customerId, macAddress) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
create table if not exists client_session (
|
|
||||||
-- postgresql
|
|
||||||
macAddress bigint ,
|
|
||||||
macAddressString varchar(100) ,
|
|
||||||
|
|
||||||
customerId int,
|
|
||||||
equipmentId bigint,
|
|
||||||
locationId bigint,
|
|
||||||
details bytea,
|
|
||||||
|
|
||||||
lastModifiedTimestamp bigint not null,
|
|
||||||
|
|
||||||
primary key (customerId, equipmentId, macAddress)
|
|
||||||
);
|
|
||||||
|
|
||||||
alter table client_session add column if not exists macAddressString varchar(100);
|
|
||||||
|
|
||||||
create index if not exists idx_clientSession_customerId on client_session (customerId);
|
|
||||||
create index if not exists idx_clientSession_locationId on client_session (customerId, locationId);
|
|
||||||
create index if not exists idx_clientSession_customerId_macAddressString on client_session (customerId, macAddressString);
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package com.telecominfraproject.wlan.client.datastore.rdbms;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
|
||||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.client.models.Client;
|
|
||||||
import com.telecominfraproject.wlan.core.model.equipment.MacAddress;
|
|
||||||
import com.telecominfraproject.wlan.core.server.jdbc.test.BaseJdbcTest;
|
|
||||||
import com.telecominfraproject.wlan.core.server.jdbc.test.TestWithEmbeddedDB;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author dtoptygin
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Import(value = {
|
|
||||||
ClientDatastoreRdbms.class,
|
|
||||||
ClientDataSourceConfig.class,
|
|
||||||
ClientDAO.class,
|
|
||||||
ClientSessionDAO.class,
|
|
||||||
BaseJdbcTest.Config.class
|
|
||||||
})
|
|
||||||
@TestWithEmbeddedDB
|
|
||||||
public class ClientDatastoreRdbmsPlumbingTests extends BaseJdbcTest {
|
|
||||||
|
|
||||||
@Autowired(required=false) private EmbeddedDatabase db;
|
|
||||||
@Autowired private ClientDatastoreRdbms clientDatastore;
|
|
||||||
@Autowired private ClientDAO clientDAO;
|
|
||||||
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDataAccess() {
|
|
||||||
if(db!=null){
|
|
||||||
//this is a simple test to see if embedded db is working in test environment
|
|
||||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(db);
|
|
||||||
Long ret = jdbcTemplate.queryForObject(
|
|
||||||
"select macAddress from client where customerId = ? and macAddress = ?",
|
|
||||||
Long.class, 1, 1);
|
|
||||||
|
|
||||||
assertEquals((Long)1L, ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateUpdateDeleteClient() {
|
|
||||||
|
|
||||||
//GET by Id test
|
|
||||||
Client ret = clientDatastore.getOrNull(1, new MacAddress(1L));
|
|
||||||
|
|
||||||
//DELETE Test
|
|
||||||
clientDAO.delete(ret.getCustomerId(), ret.getMacAddress());
|
|
||||||
|
|
||||||
assertNull(clientDatastore.getOrNull(ret.getCustomerId(), ret.getMacAddress()));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package com.telecominfraproject.wlan.client.datastore.rdbms;
|
|
||||||
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
import org.springframework.test.annotation.Rollback;
|
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.core.server.jdbc.test.BaseJdbcTest;
|
|
||||||
import com.telecominfraproject.wlan.core.server.jdbc.test.TestWithEmbeddedDB;
|
|
||||||
|
|
||||||
import com.telecominfraproject.wlan.client.datastore.BaseClientDatastoreTest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author dtoptygin
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@RunWith(SpringRunner.class)
|
|
||||||
@SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = BaseJdbcTest.Config.class)
|
|
||||||
@Rollback(value = true)
|
|
||||||
@Transactional
|
|
||||||
@Import(value = { ClientDatastoreRdbms.class, ClientDataSourceConfig.class,
|
|
||||||
ClientDAO.class, ClientSessionDAO.class, BaseJdbcTest.Config.class })
|
|
||||||
@TestWithEmbeddedDB
|
|
||||||
public class ClientDatastoreRdbmsTests extends BaseClientDatastoreTest {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<configuration>
|
|
||||||
<conversionRule conversionWord="filteredStack"
|
|
||||||
converterClass="com.telecominfraproject.wlan.server.exceptions.logback.ExceptionCompressingConverter" />
|
|
||||||
|
|
||||||
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%filteredStack%nopex</pattern>
|
|
||||||
<!-- See http://logback.qos.ch/manual/layouts.html for details -->
|
|
||||||
<!-- %ex{5} - add at the end to display only 5 levels of the exception stack trace -->
|
|
||||||
<!-- %nopex - add at the end to not display any of the exception stack traces -->
|
|
||||||
<!-- %ex{full} - add at the end to display all the levels of the exception stack trace -->
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
details: http://logback.qos.ch/manual/configuration.html#auto_configuration
|
|
||||||
|
|
||||||
runtime configuration, if need to override the defaults:
|
|
||||||
-Dlogging.config=file:///home/ec2-user/opensync/logback.xml
|
|
||||||
|
|
||||||
for log configuration debugging - use
|
|
||||||
-Dlogback.statusListenerClass=ch.qos.logback.core.status.OnConsoleStatusListener
|
|
||||||
|
|
||||||
log levels:
|
|
||||||
OFF ERROR WARN INFO DEBUG TRACE
|
|
||||||
-->
|
|
||||||
<logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
|
|
||||||
<logger name="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" level="INFO"/>
|
|
||||||
<logger name="org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer" level="INFO"/>
|
|
||||||
<logger name="org.springframework.security.web.authentication.preauth" level="OFF"/>
|
|
||||||
<logger name="com.netflix.servo.tag.aws.AwsInjectableTag" level="OFF"/>
|
|
||||||
|
|
||||||
<logger name="com.telecominfraproject" level="WARN"/>
|
|
||||||
|
|
||||||
<root level="WARN">
|
|
||||||
<appender-ref ref="stdout"/>
|
|
||||||
</root>
|
|
||||||
|
|
||||||
</configuration>
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
drop table client if exists;
|
|
||||||
drop table client_session if exists;
|
|
||||||
|
|
||||||
create table client (
|
|
||||||
-- hsqldb
|
|
||||||
macAddress bigint,
|
|
||||||
macAddressString varchar(100),
|
|
||||||
|
|
||||||
customerId int,
|
|
||||||
details varbinary(65535),
|
|
||||||
|
|
||||||
createdTimestamp bigint not null,
|
|
||||||
lastModifiedTimestamp bigint not null,
|
|
||||||
|
|
||||||
primary key (customerId, macAddress)
|
|
||||||
);
|
|
||||||
|
|
||||||
create index idx_client_customerId on client (customerId);
|
|
||||||
create index if not exists idx_client_customerId_macAddressString on client (customerId, macAddressString);
|
|
||||||
|
|
||||||
create table if not exists client_blocklist (
|
|
||||||
-- hsqldb
|
|
||||||
customerId int,
|
|
||||||
macAddress bigint ,
|
|
||||||
|
|
||||||
primary key (customerId, macAddress),
|
|
||||||
FOREIGN KEY (customerId, macAddress) REFERENCES client(customerId, macAddress) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
create table client_session (
|
|
||||||
-- postgresql
|
|
||||||
macAddress bigint ,
|
|
||||||
macAddressString varchar(100),
|
|
||||||
|
|
||||||
customerId int,
|
|
||||||
equipmentId bigint,
|
|
||||||
locationId bigint,
|
|
||||||
details varbinary(65535),
|
|
||||||
|
|
||||||
lastModifiedTimestamp bigint not null,
|
|
||||||
|
|
||||||
primary key (customerId, equipmentId, macAddress)
|
|
||||||
);
|
|
||||||
|
|
||||||
create index idx_clientSession_customerId on client_session (customerId);
|
|
||||||
create index idx_clientSession_locationId on client_session (customerId, locationId);
|
|
||||||
create index if not exists idx_clientSession_customerId_macAddressString on client_session (customerId, macAddressString);
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
insert into client (
|
|
||||||
macAddress,
|
|
||||||
macAddressString,
|
|
||||||
customerId,
|
|
||||||
details,
|
|
||||||
createdTimestamp,
|
|
||||||
lastModifiedTimestamp
|
|
||||||
) values (
|
|
||||||
1,
|
|
||||||
'1',
|
|
||||||
1,
|
|
||||||
null,
|
|
||||||
0,0
|
|
||||||
);
|
|
||||||
1
client-models/.gitignore
vendored
Normal file
1
client-models/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
client-service-interface/.gitignore
vendored
Normal file
1
client-service-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
client-service-local/.gitignore
vendored
Normal file
1
client-service-local/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
client-service-remote/.gitignore
vendored
Normal file
1
client-service-remote/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
client-service/.gitignore
vendored
Normal file
1
client-service/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
cloud-event-dispatcher-empty/.gitignore
vendored
Normal file
1
cloud-event-dispatcher-empty/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
cloud-event-dispatcher-interface/.gitignore
vendored
Normal file
1
cloud-event-dispatcher-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
cloud-event-dispatcher-local/.gitignore
vendored
Normal file
1
cloud-event-dispatcher-local/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
cloud-event-dispatcher-remote/.gitignore
vendored
Normal file
1
cloud-event-dispatcher-remote/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
cloud-event-dispatcher/.gitignore
vendored
Normal file
1
cloud-event-dispatcher/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
@@ -136,7 +136,6 @@
|
|||||||
<module>../client-datastore-common-test</module>
|
<module>../client-datastore-common-test</module>
|
||||||
<module>../client-datastore-inmemory</module>
|
<module>../client-datastore-inmemory</module>
|
||||||
<module>../client-datastore-interface</module>
|
<module>../client-datastore-interface</module>
|
||||||
<module>../client-datastore-rdbms</module>
|
|
||||||
<module>../client-datastore-cassandra</module>
|
<module>../client-datastore-cassandra</module>
|
||||||
<module>../client-models</module>
|
<module>../client-models</module>
|
||||||
<module>../client-service</module>
|
<module>../client-service</module>
|
||||||
|
|||||||
1
customer-datastore-common-test/.gitignore
vendored
Normal file
1
customer-datastore-common-test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
customer-datastore-inmemory/.gitignore
vendored
Normal file
1
customer-datastore-inmemory/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
customer-datastore-interface/.gitignore
vendored
Normal file
1
customer-datastore-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
customer-datastore-rdbms/.gitignore
vendored
Normal file
1
customer-datastore-rdbms/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
customer-models/.gitignore
vendored
Normal file
1
customer-models/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
customer-service-interface/.gitignore
vendored
Normal file
1
customer-service-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
customer-service-local/.gitignore
vendored
Normal file
1
customer-service-local/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
customer-service-remote/.gitignore
vendored
Normal file
1
customer-service-remote/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
customer-service/.gitignore
vendored
Normal file
1
customer-service/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
dashboard-sp/.gitignore
vendored
Normal file
1
dashboard-sp/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-alarms-sp/.gitignore
vendored
Normal file
1
equipment-alarms-sp/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-datastore-common-test/.gitignore
vendored
Normal file
1
equipment-datastore-common-test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-datastore-inmemory/.gitignore
vendored
Normal file
1
equipment-datastore-inmemory/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-datastore-interface/.gitignore
vendored
Normal file
1
equipment-datastore-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-datastore-rdbms/.gitignore
vendored
Normal file
1
equipment-datastore-rdbms/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-gateway-models/.gitignore
vendored
Normal file
1
equipment-gateway-models/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-gateway-service-empty/.gitignore
vendored
Normal file
1
equipment-gateway-service-empty/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-gateway-service-interface/.gitignore
vendored
Normal file
1
equipment-gateway-service-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-gateway-service-local/.gitignore
vendored
Normal file
1
equipment-gateway-service-local/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-gateway-service-remote/.gitignore
vendored
Normal file
1
equipment-gateway-service-remote/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-models/.gitignore
vendored
Normal file
1
equipment-models/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-service-interface/.gitignore
vendored
Normal file
1
equipment-service-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-service-local/.gitignore
vendored
Normal file
1
equipment-service-local/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-service-remote/.gitignore
vendored
Normal file
1
equipment-service-remote/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
equipment-service/.gitignore
vendored
Normal file
1
equipment-service/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
filestore-service/.gitignore
vendored
Normal file
1
filestore-service/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
firmware-datastore-common-test/.gitignore
vendored
Normal file
1
firmware-datastore-common-test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
firmware-datastore-inmemory/.gitignore
vendored
Normal file
1
firmware-datastore-inmemory/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
firmware-datastore-interface/.gitignore
vendored
Normal file
1
firmware-datastore-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
firmware-datastore-rdbms/.gitignore
vendored
Normal file
1
firmware-datastore-rdbms/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
firmware-models/.gitignore
vendored
Normal file
1
firmware-models/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
firmware-service-interface/.gitignore
vendored
Normal file
1
firmware-service-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
firmware-service-local/.gitignore
vendored
Normal file
1
firmware-service-local/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
firmware-service-remote/.gitignore
vendored
Normal file
1
firmware-service-remote/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
firmware-service/.gitignore
vendored
Normal file
1
firmware-service/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
kafka-streams-consumer/.gitignore
vendored
Normal file
1
kafka-streams-consumer/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
kafka-streams/.gitignore
vendored
Normal file
1
kafka-streams/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
location-datastore-common-test/.gitignore
vendored
Normal file
1
location-datastore-common-test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
location-datastore-inmemory/.gitignore
vendored
Normal file
1
location-datastore-inmemory/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
location-datastore-interface/.gitignore
vendored
Normal file
1
location-datastore-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
location-datastore-rdbms/.gitignore
vendored
Normal file
1
location-datastore-rdbms/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
location-models/.gitignore
vendored
Normal file
1
location-models/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
location-service-interface/.gitignore
vendored
Normal file
1
location-service-interface/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
1
location-service-local/.gitignore
vendored
Normal file
1
location-service-local/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user