Storage. Начало. Уже скомпилируется.

This commit is contained in:
AKurakin 2022-06-30 19:43:10 +03:00
parent 29828d8f07
commit e3e2a27a82
46 changed files with 2264 additions and 4 deletions

37
clearing-classes/pom.xml Normal file
View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<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>
<artifactId>clearing-classes</artifactId>
<name>Clearing classes</name>
<description>Clearing classes module</description>
<packaging>jar</packaging>
<parent>
<artifactId>clearing</artifactId>
<groupId>ru.spcex.clearing</groupId>
<version>SPCEX-1.0.0.0</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>com.moex.platform</groupId>-->
<!-- <artifactId>platform-common</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.moex.platform</groupId>-->
<!-- <artifactId>platform-errors</artifactId>-->
<!-- </dependency>-->
</dependencies>
<build>
<finalName>${project.artifactId}-${project.version}</finalName>
</build>
</project>

View file

@ -0,0 +1,8 @@
package ru.clearing.classes;
/**
* В случае любых изменений модуля classes необходимо изменить serialVersionUID++
*/
public interface ConstSerializable {
long serialVersionUID = 293236453420L;
}

View file

@ -0,0 +1,59 @@
package ru.clearing.classes.StaticData.Company;
import ru.clearing.classes.ConstSerializable;
import ru.clearing.classes.objects.BusinessObject;
/**
*
*/
public class Company extends BusinessObject {
private static final long serialVersionUID = ConstSerializable.serialVersionUID;
private String clearingCode;
private String exchangeCode;
private String fullName;
// private CompanyInfo profile; // todo impl profile
private Long statusId; // WorkflowStatusDictionary
@Override
public String toString() {
return "Company{" +
"clearingCode='" + clearingCode + '\'' +
", exchangeCode='" + exchangeCode + '\'' +
", fullName='" + fullName + '\'' +
", statusId=" + statusId +
'}';
}
public String getClearingCode() {
return clearingCode;
}
public void setClearingCode(String clearingCode) {
this.clearingCode = clearingCode;
}
public String getExchangeCode() {
return exchangeCode;
}
public void setExchangeCode(String exchangeCode) {
this.exchangeCode = exchangeCode;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Long getStatusId() {
return statusId;
}
public void setStatusId(Long statusId) {
this.statusId = statusId;
}
}

View file

@ -0,0 +1,30 @@
package ru.clearing.classes.StaticData.Company;
import ru.clearing.classes.ConstSerializable;
import ru.clearing.classes.objects.ObjectBase;
/**
* DB: COMPANY_ROLE_SET
*/
public class CompanyRoleSet extends ObjectBase {
private static final long serialVersionUID = ConstSerializable.serialVersionUID;
private Long roleId;
private Long companyId;
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public Long getCompanyId() {
return companyId;
}
public void setCompanyId(Long companyId) {
this.companyId = companyId;
}
}

View file

@ -0,0 +1,27 @@
package ru.clearing.classes.StaticData.User;
import ru.clearing.classes.ConstSerializable;
import ru.clearing.classes.objects.BusinessObject;
/**
* Database table: USER_CLEARING todo name for user ?
*/
public class ClearingUser extends BusinessObject {
private static final long serialVersionUID = ConstSerializable.serialVersionUID;
// private String idpUuid;
//
// public ClearingUser() {
// super();
// }
//
// public String toString() {
// return String.format("%s{id=%d, idpUuid='%s'}", getClass().getSimpleName(), getId(), getIdpUuid());
// }
//
// public String getIdpUuid() {
// return idpUuid;
// }
}

View file

@ -0,0 +1,25 @@
package ru.clearing.classes.objects;
import ru.clearing.classes.ConstSerializable;
import java.util.Date;
/**
*
*/
public abstract class BusinessEvent extends ObjectBase {
static final long serialVersionUID = ConstSerializable.serialVersionUID;
private Date eventTime;
public BusinessEvent() {
}
public Date getEventTime() {
return eventTime;
}
public void setEventTime(Date eventTime) {
this.eventTime = eventTime;
}
}

View file

@ -0,0 +1,31 @@
package ru.clearing.classes.objects;
import ru.clearing.classes.ConstSerializable;
import java.time.Instant;
/**
* UUID - Long based with field object
*/
public abstract class BusinessObject extends ObjectBase {
static final long serialVersionUID = ConstSerializable.serialVersionUID;
private Instant created;
private Instant updated;
public Instant getCreated() {
return created;
}
public void setCreated(Instant created) {
this.created = created;
}
public Instant getUpdated() {
return updated;
}
public void setUpdated(Instant updated) {
this.updated = updated;
}
}

View file

@ -0,0 +1,55 @@
package ru.clearing.classes.objects;
import ru.clearing.classes.ConstSerializable;
import ru.clearing.common.interfaces.WithId;
import java.io.Serializable;
/**
* Long ID - based primary key object
*/
public abstract class ObjectBase implements WithId, Serializable {
static final long serialVersionUID = ConstSerializable.serialVersionUID;
protected Long id;
public ObjectBase(){
}
public ObjectBase(Long id) {
this.id = id;
}
public static Long getIdOrNull(WithId o) {
return o == null ? null : o.getId();
}
@Override
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public String toString() {
return String.format("%s{id=%d}", getClass().getSimpleName(), getId());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ObjectBase otcObject = (ObjectBase) o;
return id.equals(otcObject.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
}

View file

@ -0,0 +1,5 @@
package ru.clearing.common.interfaces;
public interface WithId {
Long getId();
}

Binary file not shown.

View file

@ -0,0 +1,4 @@
#Created by Apache Maven 3.6.3
groupId=ru.spcex.clearing
artifactId=clearing-classes
version=SPCEX-1.0.0.0

View file

@ -0,0 +1,8 @@
ru\clearing\classes\objects\ObjectBase.class
ru\clearing\classes\ConstSerializable.class
ru\clearing\classes\StaticData\Company\CompanyRoleSet.class
ru\clearing\classes\objects\BusinessEvent.class
ru\clearing\classes\StaticData\Company\Company.class
ru\clearing\classes\StaticData\User\ClearingUser.class
ru\clearing\common\interfaces\WithId.class
ru\clearing\classes\objects\BusinessObject.class

View file

@ -0,0 +1,8 @@
E:\MFD_GIT_REPO\clearing\clearing-classes\src\main\java\ru\clearing\classes\StaticData\User\ClearingUser.java
E:\MFD_GIT_REPO\clearing\clearing-classes\src\main\java\ru\clearing\classes\objects\ObjectBase.java
E:\MFD_GIT_REPO\clearing\clearing-classes\src\main\java\ru\clearing\classes\ConstSerializable.java
E:\MFD_GIT_REPO\clearing\clearing-classes\src\main\java\ru\clearing\classes\StaticData\Company\Company.java
E:\MFD_GIT_REPO\clearing\clearing-classes\src\main\java\ru\clearing\classes\objects\BusinessEvent.java
E:\MFD_GIT_REPO\clearing\clearing-classes\src\main\java\ru\clearing\classes\objects\BusinessObject.java
E:\MFD_GIT_REPO\clearing\clearing-classes\src\main\java\ru\clearing\classes\StaticData\Company\CompanyRoleSet.java
E:\MFD_GIT_REPO\clearing\clearing-classes\src\main\java\ru\clearing\common\interfaces\WithId.java

12
clearing-dictionary/.gitignore vendored Normal file
View file

@ -0,0 +1,12 @@
*.iml
target
lib
.idea
*.log
temp
otc.logs_IS_UNDEFINED
.gitconfig
dependency-reduced-pom.xml
*/otc-test/src/test/resources/dataForDb/*.sql
**/out
preview

View file

@ -0,0 +1,3 @@
1.0.0
------------------------------
-

118
clearing-dictionary/pom.xml Normal file
View file

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<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>
<artifactId>clearing-dictionary</artifactId>
<name>PLATFORM dictionary</name>
<description>PLATFORM dictionary</description>
<packaging>jar</packaging>
<version>SPCEX-1.0.0.0</version>
<parent>
<artifactId>clearing</artifactId>
<groupId>ru.spcex.clearing</groupId>
<version>SPCEX-1.0.0.0</version>
</parent>
<properties>
<external_libraries.junit-jupiter.version>5.4.2</external_libraries.junit-jupiter.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- TEST -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${external_libraries.junit-jupiter.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>${project.java.version}</source>
<target>${project.java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<manifestEntries>
<Built-By>${built.by}</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<manifestEntries>
<Built-By>${built.by}</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
</plugins>
</build>
</project>

View file

@ -0,0 +1,29 @@
package ru.clearing.platform.dictionary;
public abstract class AbstractDictionary implements Dictionary {
static final long serialVersionUID = ConstDictionarySerializable.serialVersionUID;
protected Long id;
protected String name;
@Override
public Long getId() {
return id;
}
@Override
public void setId(Long id) {
this.id = id;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
}

View file

@ -0,0 +1,5 @@
package ru.clearing.platform.dictionary;
public interface ConstDictionarySerializable {
long serialVersionUID = 7020097431335696640L;
}

View file

@ -0,0 +1,17 @@
package ru.clearing.platform.dictionary;
import java.io.Serializable;
/**
* Словарь.
*/
public interface Dictionary extends Serializable {
Long getId();
void setId(Long id);
String getName();
void setName(String id);
}

View file

@ -0,0 +1,9 @@
package ru.clearing.platform.dictionary;
/**
* Database table: WORKFLOW_STATUS_DICTIONARY
*/
public class WorkflowStatusDictionary extends AbstractDictionary {
private static final long serialVersionUID = ConstDictionarySerializable.serialVersionUID;
}

View file

@ -0,0 +1,47 @@
package ru.clearing.platform.dictionary.special;
import ru.clearing.platform.dictionary.ConstDictionarySerializable;
import ru.clearing.platform.dictionary.Dictionary;
import java.io.Serializable;
/**
* DB table: COUNTRY_CODE_DICTIONARY
* Dictionary
*/
public class CountryCode implements Serializable, Dictionary {
static final long serialVersionUID = ConstDictionarySerializable.serialVersionUID;
private Long id;
private String name;
private String countryCode;
@Override
public String toString() {
return String.format("CountryCode{id=%d, countryCode='%s'}", id, countryCode);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
}

View file

@ -15,6 +15,8 @@
<module>frontendapi</module>
<module>storage</module>
<module>db-scripts</module>
<module>clearing-classes</module>
<module>clearing-dictionary</module>
</modules>
<properties>
@ -27,6 +29,9 @@
<global.project.version>SPCEX-1.0.0.0</global.project.version>
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
<!-- IMDG -->
<external_libraries.hazelcast.version>3.12.4</external_libraries.hazelcast.version>
</properties>
<dependencyManagement>

View file

@ -17,10 +17,6 @@
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
@ -29,6 +25,38 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>ru.spcex.clearing</groupId>
<artifactId>clearing-classes</artifactId>
<version>SPCEX-1.0.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ru.spcex.clearing</groupId>
<artifactId>clearing-dictionary</artifactId>
<version>SPCEX-1.0.0.0</version>
<scope>compile</scope>
</dependency>
<!-- JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- IMDG для MapStore -->
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-all</artifactId>
<version>${external_libraries.hazelcast.version}</version>
</dependency>
</dependencies>
<build>
<finalName>jar/${project.artifactId}</finalName>

View file

@ -0,0 +1,61 @@
package ru.spcex.clearing.storage.base;
import org.springframework.jdbc.core.JdbcTemplate;
import ru.clearing.classes.objects.BusinessEvent;
import ru.clearing.classes.objects.BusinessObject;
import ru.spcex.clearing.storage.utils.TimeUtil;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
public abstract class BusinessEventMapStore<T extends BusinessEvent> extends ObjectBaseMapStore<T> {
public BusinessEventMapStore(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
@Override
public final Iterable<Long> loadAllKeys() {
return Collections.emptyList();
}
@Override
public final T load(Long id) {
return null;
}
@Override
public final Collection<T> load(Collection<Long> keys) {
return Collections.emptyList();
}
@Override
public final Map<Long, T> loadAll(Collection<Long> keys) {
return Collections.emptyMap();
}
/**
* Заполняет поля:<br>
* uuid<br>
* eventTime<br>
* ownerId<br>
* eventTypeId
*
* @param businessEvent объект для заполнения
* @param rs resultSet для выгрузки
*/
public void fillBusinessEventFields(BusinessEvent businessEvent, ResultSet rs) throws SQLException {
businessEvent.setEventTime(TimeUtil.toUtilDate(rs.getDate("eventtime")));
businessEvent.setId(getLong(rs, "id"));
}
public void fillBusinessObjectFieds(BusinessObject businessObject, ResultSet rs) throws SQLException {
businessObject.setId(rs.getObject("orderbondid", Long.class));
businessObject.setUpdated(TimeUtil.fromDate(rs.getDate("updated")));
businessObject.setCreated(TimeUtil.fromDate(rs.getDate("created")));
}
}

View file

@ -0,0 +1,35 @@
package ru.spcex.clearing.storage.base;
import org.springframework.jdbc.core.JdbcTemplate;
import ru.clearing.classes.objects.BusinessObject;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Шаблон для загрузки <code>BusinessObject</code>-ов.
*/
public abstract class BusinessObjectMapStore<T extends BusinessObject> extends ObjectBaseMapStore<T> {
public BusinessObjectMapStore(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
/**
* Заполняет стандартные поля для BusinessObject из ResultSet
*
* @param businessObject дозаполнить стандартные поля этому объекту
* @param rs из ResultSet
* @throws SQLException - error
*/
protected void fillBusinessObjectFields(BusinessObject businessObject, ResultSet rs) throws SQLException {
fillBusinessObjectFields(businessObject, rs, "id");
}
protected void fillBusinessObjectFields(BusinessObject businessObject, ResultSet rs, String idName) throws SQLException {
businessObject.setUpdated(getInstantFromTimestamp(rs, "updated"));
businessObject.setCreated(getInstantFromTimestamp(rs, "created"));
businessObject.setId(getLong(rs, idName));
}
}

View file

@ -0,0 +1,91 @@
package ru.spcex.clearing.storage.base;
import com.hazelcast.core.MapLoader;
import ru.clearing.platform.dictionary.Dictionary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import static ru.spcex.clearing.storage.base.ObjectBaseMapStore.MAX_IN_CLAUSE_SIZE;
/**
* Store для словарей стандартных - из ID, NAME.
*/
public abstract class DictionaryMapStore<T extends Dictionary> implements MapLoader<Long, T> {
private static Logger log = LoggerFactory.getLogger(DictionaryMapStore.class);
protected NamedParameterJdbcTemplate namedParameterJdbcTemplate;
protected final JdbcTemplate jdbcTemplate;
protected DictionaryMapStore(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
}
public abstract String getTableName();
public abstract T getDictionaryObject();
protected T mapRow(ResultSet rs, int rowNum) throws SQLException {
T dictionary = getDictionaryObject();
dictionary.setId(rs.getLong("id"));
dictionary.setName(rs.getString("name"));
return dictionary;
}
private Collection<T> loadCollection(Collection<Long> keys) {
Map<String, Collection<Long>> paramMap = Collections.singletonMap("ids", keys);
return namedParameterJdbcTemplate.query("select * from " + getTableName() + " where id in (:ids)", paramMap, this::mapRow);
}
@Override
public T load(Long id) {
Collection<T> rows;
List<Long> list = Collections.singletonList(id);
try {
rows = loadCollection(list);
} catch (Throwable e) { // one retry
log.trace("Retry load after {}", e.toString());
rows = loadCollection(list);
}
return DataAccessUtils.singleResult(rows);
}
@Override
public Map<Long, T> loadAll(Collection<Long> keys) {
log.trace("loadAll from dictionary {} {} keys", getTableName(), keys.size());
Map<Long, T> result = new HashMap<>();
long start = System.currentTimeMillis();
// загрузить данные по ключам частями, чтобы не выйти за ограничения базы по кол-ву элементов в in clause
List<Long> keysSubList = new ArrayList<>(MAX_IN_CLAUSE_SIZE);
for (Iterator<Long> iterator = keys.iterator(); iterator.hasNext(); ) {
Long key = iterator.next();
keysSubList.add(key);
if (keysSubList.size() == MAX_IN_CLAUSE_SIZE || !iterator.hasNext()) {
Collection<T> rows = loadCollection(keysSubList);
for (T row : rows) {
result.put(row.getId(), row);
}
keysSubList.clear();
}
}
log.trace("loadAll from dictionary {} {} keys done in {} ms", getTableName(), keys.size(), (System.currentTimeMillis() - start));
return result;
}
@Override
public Iterable<Long> loadAllKeys() {
log.debug("loadAllKeys from " + getTableName());
return jdbcTemplate.query("select id from " + getTableName(), (rs, rowNum) -> rs.getLong("id"));
}
}

View file

@ -0,0 +1,94 @@
package ru.spcex.clearing.storage.base;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.MapLoaderLifecycleSupport;
import com.hazelcast.core.MapStore;
//import com.opencsv.CSVWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
import ru.clearing.classes.objects.ObjectBase;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.function.Supplier;
/**
* MapStore записывающий изменения в файл
*
* @param <T>
*/
public abstract class LoggingMapStore<T extends ObjectBase> implements MapStore<Long, T>, MapLoaderLifecycleSupport {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
// private CSVWriter csvWriter; todo подключить OpenCSV и настройку про логирование данных добавить
// private Supplier<String> recoveryPath = () -> DfaConfig.get().getRoot().getSettings().getRecoveryLogPath();
@Override
public void init(HazelcastInstance hazelcastInstance, Properties properties, String mapName) {
// try {
// String recoveryLogPath = recoveryPath.get();
// if (recoveryLogPath != null) {
// SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
// File recoveryDir = new File(recoveryLogPath + File.separator + simpleDateFormat.format(new Date()));
// recoveryDir.mkdirs();
// File recoveryFile = new File(recoveryDir, getTableName() + ".csv");
// csvWriter = new CSVWriter(new BufferedWriter(new FileWriter(recoveryFile)), ';');
// log.info("recovery log started {}", recoveryFile);
// }
// } catch (Exception e) {
// log.error("", e);
// }
}
// public void setRecoveryPath(Supplier<String> recoveryPath) {
// this.recoveryPath = recoveryPath;
// }
@Override
public void destroy() {
// try {
// if (csvWriter != null)
// csvWriter.close();
// } catch (IOException e) {
// log.error("", e);
// }
}
public abstract String getTableName();
protected void logHeaders(@NonNull String[] headers) {
try {
// if (csvWriter != null) {
// csvWriter.writeNext(headers);
// csvWriter.flush();
// }
} catch (Exception e) {
log.error("", e);
}
}
protected void logValues(@NonNull List<Object[]> valuesList) {
try {
// if (csvWriter != null) {
// for (Object[] values : valuesList) {
// String[] nextLine = new String[values.length];
// for (int i = 0; i < values.length; i++) {
// nextLine[i] = String.valueOf(values[i]);
// }
// csvWriter.writeNext(nextLine);
// }
// csvWriter.flush();
// }
} catch (Exception e) {
log.error("", e);
}
}
}

View file

@ -0,0 +1,174 @@
package ru.spcex.clearing.storage.base;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import ru.clearing.classes.objects.ObjectBase;
import ru.spcex.clearing.storage.utils.DbUtilsHelper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.*;
public abstract class ObjectBaseMapStore<T extends ObjectBase> extends SimpleObjectMapStore<T> {
/**
* См. legalEntityProfileToSQLArgs()
*/
protected static final String LEGAL_ENTITY_PROFILE_STATEMENT_ARGUMENT =
"description, domicilecountrycodeid, isprofessional, organizationtypeid, ogrn, name," +
" fullname, nameeng, fullnameeng, kpp, contactphone, contactemail, tsedallowedid, edomail," +
" nsdallowedid, otcmonitorallowedid, otcmonitorobligedid";
protected static final String insertChargeDefinitionStatement = DbUtilsHelper.fillValuesBlock(
"UPDATE OR INSERT INTO CHARGEDEFINITION (id, chargeamount, chargedirectionid, " +
" chargesettlementamount, chargetypeid, netincluded, currencycodeid, moexexchangecommission, userid, partyid)" +
" values %values_block% matching (id);"
);
public static final int MAX_IN_CLAUSE_SIZE = 1000;
protected NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public ObjectBaseMapStore(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
}
// @Override
// public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
// this.jdbcTemplate = jdbcTemplate;
// this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
// }
@Override
public T load(Long id) {
if (!isLoadable(id)) return null;
Collection<T> rows;
List<Long> list = Collections.singletonList(id);
try {
rows = load(list);
} catch (Throwable e) { // one retry
rows = load(list);
}
T obj = DataAccessUtils.singleResult(rows);
if (obj != null && isLoadable(obj))
return obj;
else
return null;
}
protected boolean isLoadable(Long id) {
return true;
}
protected boolean isLoadable(T obj) {
return true;
}
@Override
public Map<Long, T> loadAll(Collection<Long> keys) {
log.debug("loadAll from " + getTableName() + " " + keys.size() + " keys");
Map<Long, T> result = new HashMap<>();
long start = System.currentTimeMillis();
// загрузить данные по ключам частями, чтобы не выйти за ограничения базы по кол-ву элементов в in clause
List<Long> keysSubList = new ArrayList<>(MAX_IN_CLAUSE_SIZE);
for (Iterator<Long> iterator = keys.iterator(); iterator.hasNext(); ) {
Long key = iterator.next();
keysSubList.add(key);
if (keysSubList.size() == MAX_IN_CLAUSE_SIZE || !iterator.hasNext()) {
Collection<T> rows = load(keysSubList);
for (T row : rows) {
result.put(row.getId(), row);
}
keysSubList.clear();
}
}
log.debug("loadAll from " + getTableName() + " " + keys.size() + " keys done in " + (System.currentTimeMillis() - start) + "ms");
return result;
}
public abstract Collection<T> load(Collection<Long> keys);
protected Long getLong(ResultSet rs, String columnName) throws SQLException {
return rs.getObject(columnName, Long.class);
}
protected Instant getInstantFromTimestamp(ResultSet rs, String column) throws SQLException {
Timestamp date = rs.getTimestamp(column);
return date != null ? date.toInstant() : null;
}
/* todo реfакторинг:
protected LegalEntityProfile getLegalEntityProfile(Long id) {
if (id == null)
return null;
List<LegalEntityProfile> results = jdbcTemplate.query(
"SELECT * FROM legalentityprofile WHERE id = ?", new Object[]{id},
(rs, rowNum) -> {
LegalEntityProfile legalEntityProfile = new LegalEntityProfile();
legalEntityProfile.setDescription(rs.getString("description"));
legalEntityProfile.setDomicileCountryCodeId(rs.getObject("domicilecountrycodeid", Long.class));
legalEntityProfile.setProfessional(rs.getObject("isprofessional", Boolean.class));
legalEntityProfile.setOrganizationTypeId(rs.getObject("organizationtypeid", Long.class));
legalEntityProfile.setOgrn(rs.getString("ogrn"));
legalEntityProfile.setShortName(rs.getString("name"));
legalEntityProfile.setFullName(rs.getString("fullname"));
legalEntityProfile.setNameEng(rs.getString("nameeng"));
legalEntityProfile.setFullNameEng(rs.getString("fullnameeng"));
legalEntityProfile.setKpp(rs.getString("kpp"));
legalEntityProfile.setContactPhone(rs.getString("contactphone"));
legalEntityProfile.setContactEMail(rs.getString("contactemail"));
legalEntityProfile.setTsedAllowedId(rs.getObject("tsedallowedid", Long.class));
legalEntityProfile.setEdoMail(rs.getString("edomail"));
legalEntityProfile.setNsdAllowedId(rs.getObject("nsdallowedid", Long.class));
legalEntityProfile.setOtcMonitorAllowedId(rs.getObject("otcmonitorallowedid", Long.class));
legalEntityProfile.setOtcMonitorObligedId(rs.getObject("otcmonitorobligedid", Long.class));
legalEntityProfile.setId(getLong(rs, "id"));
return legalEntityProfile;
}
);
LegalEntityProfile onceObject = DataAccessUtils.singleResult(results);
if (onceObject == null) {
log.warn("No row in table legalentityprofile where id={}. Create empty ", id);
onceObject = new LegalEntityProfile();
onceObject.setId(id); // чтобы не потерять id
}
return onceObject;
}
*/
// /**
// * Без ID перечисление значений полей LegalEntityProfile.
// * См. LegalentityprofileStatementArgument
// *
// * @param legalEntityProfile
// * @return
// */
// protected List<Object> legalEntityProfileToSQLArgs(@NonNull LegalEntityProfile legalEntityProfile) {
// List<Object> legalEntityArgs = new ArrayList<>(Arrays.asList(
//// legalEntityProfile.getId(),
// legalEntityProfile.getDescription(),
// legalEntityProfile.getDomicileCountryCodeId(),
// legalEntityProfile.getProfessional(),
// legalEntityProfile.getOrganizationTypeId(),
// legalEntityProfile.getOgrn(),
// legalEntityProfile.getShortName(),
// legalEntityProfile.getFullName(),
// legalEntityProfile.getNameEng(),
// legalEntityProfile.getFullNameEng(),
// legalEntityProfile.getKpp(),
// legalEntityProfile.getContactPhone(),
// legalEntityProfile.getContactEMail(),
// legalEntityProfile.getTsedAllowedId(),
// legalEntityProfile.getEdoMail(),
// legalEntityProfile.getNsdAllowedId(),
// legalEntityProfile.getOtcMonitorAllowedId(),
// legalEntityProfile.getOtcMonitorObligedId()
// ));
// return legalEntityArgs;
// }
}

View file

@ -0,0 +1,200 @@
package ru.spcex.clearing.storage.base;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlTypeValue;
import org.springframework.jdbc.core.StatementCreatorUtils;
import org.springframework.lang.NonNull;
import ru.clearing.classes.objects.ObjectBase;
import ru.spcex.clearing.storage.utils.BigDecimalUtil;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.*;
/**
* MapStore для выгрузки обычных объектов
*
* @param <T>
*/
public abstract class SimpleObjectMapStore<T extends ObjectBase> extends LoggingMapStore<T>
{
protected final Logger log = LoggerFactory.getLogger(this.getClass());
protected SimpleDateFormat FIREBIRD_DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd"); // не делать static.
protected final JdbcTemplate jdbcTemplate;
private final int bathSize = 1000;
protected SimpleObjectMapStore(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
logHeaders(getFields());
}
public abstract String getTableName();
public abstract String[] getFields();
private boolean oidIsPresentInTheFields = Arrays.stream(getFields()).anyMatch(anotherString -> {
if (anotherString == null) return false;
return "oid".equalsIgnoreCase(anotherString.trim());
});
public boolean deleteIsSupported() {
return !oidIsPresentInTheFields;
}
@Override
public final void store(Long id, T o) {
log.debug("store {} {} {}", getTableName(), id, o);
Map<Long, T> map = new HashMap<>();
map.put(id, o);
store(map);
}
@Override
public final void storeAll(Map<Long, T> map) {
log.debug("storeAll {} {}", getTableName(), map);
store(map);
}
public abstract void store(Map<Long, T> map);
@Override
public void delete(Long id) {
if (deleteIsSupported()) {
defaultDelete(id);
} else {
throw new UnsupportedOperationException("delete not supported");
}
}
@Override
public void deleteAll(Collection<Long> collection) {
if (deleteIsSupported()) {
deleteAllShowNotDeleted(collection);
} else {
throw new UnsupportedOperationException("deleteAll not supported");
}
}
protected void defaultDelete(Long id) {
ArrayList<Long> list = new ArrayList<>();
list.add(id);
deleteAll(list);
}
protected void deleteAllShowNotDeleted(Collection<Long> collection) {
List<Object[]> listOfID = new ArrayList<>(collection.size());
for (Long id : collection)
listOfID.add(new Object[]{id});
try {
jdbcTemplate.batchUpdate("DELETE FROM " + getTableName() + " WHERE id=?", listOfID);
} catch (Exception e) {
log.error("SQL error at batch DELETE FROM {}\n{}", getTableName(), ExceptionUtils.getStackTrace(e));
log.info("List of non deleted ID {}", collection); // показать список неудалённых UUID
throw e;
}
}
@Override
public abstract T load(Long id);
@Override
public Map<Long, T> loadAll(Collection<Long> keys) {
Map<Long, T> result = new HashMap<>();
for (Long key : keys) {
result.put(key, load(key));
}
return result;
}
@Override
public Iterable<Long> loadAllKeys() {
log.debug("loadAllKeys from " + getTableName());
List<Long> keys;
try {
keys = jdbcTemplate.query("select id from " + getTableName(), (resultSet, i) -> resultSet.getLong("id"));
} catch (Throwable e) {
log.error("{}", ExceptionUtils.getStackTrace(e));
throw e;
}
return keys;
}
/**
* Загружает ключи только за текущий день.
* <code>where CAST(tradingday as DATE) = ?</code>
* Осторожно со знаком: в некоторых случаях требуется за текущий и будущие дни, тогда этот метод не подходит.
* См. так же loadAllKeys().
*
* @return список id
*/
protected List<Long> defaultLoadAllKeysOnTradingDay() {
log.debug("loadAllKeys from " + getTableName());
return jdbcTemplate.query("select id from " + getTableName() + " where CAST(tradingday as DATE) = ?",
new Object[]{FIREBIRD_DATE_FORMATTER.format(new Date())},
(resultSet, i) -> resultSet.getObject("id", Long.class));
}
protected void batchInsertUpdate(@NonNull String insertStatement, @NonNull List<Object[]> args) {
try {
logValues(args);
int[][] ret = jdbcTemplate.batchUpdate(insertStatement, args, bathSize, (ps, params) -> {
for (int i = 0; i < params.length; i++) {
int type = ps.getParameterMetaData().getParameterType(i + 1);
Object value = params[i];
if (value != null) {
if ((type == Types.VARCHAR || type == Types.CHAR) && value instanceof String) {
String stringValue = (String) value;
int length = ps.getParameterMetaData().getPrecision(i + 1);
if (stringValue.length() > length) {
value = stringValue.substring(0, length);
log.error("String value for insert is too large [ insertSql: {}, params: {}, incorrect value : {}, normalized value: {} ]",
insertStatement, Arrays.toString(params), stringValue, value);
}
} else if ((type == Types.DECIMAL || type == Types.NUMERIC) && value instanceof BigDecimal) {
BigDecimal bigDecimalValue = (BigDecimal) value;
int dbScalePartLength = ps.getParameterMetaData().getScale(i + 1);
int precision = ps.getParameterMetaData().getPrecision(i + 1);
if (!BigDecimalUtil.checkSize(bigDecimalValue, precision, dbScalePartLength)) {
value = BigDecimalUtil.genMaxValueForInsert(precision, dbScalePartLength);
log.error("BigDecimal value for insert is too large [ insertSql: {}, params: {}, incorrect value : {}, normalized value: {} ]",
insertStatement, Arrays.toString(params), bigDecimalValue, value);
}
}
}
//в batchUpdate(String sql, List<Object[]> batchArgs), как типа поля всегда передавался SqlTypeValue.TYPE_UNKNOWN,
//см. BatchUpdateUtils.setStatementParameters
try {
StatementCreatorUtils.setParameterValue(ps, i + 1, SqlTypeValue.TYPE_UNKNOWN, value);
} catch (SQLException e) {
throw new SQLException("batchInsertUpdate, at prepare argument " + i + " value = " + value + "; SQL=" + insertStatement, e);
}
}
});
int batchSize = ret.length > 0 ? ret[0].length : ret.length;
log.debug("{} {} rows stored", getTableName(), batchSize);
} catch (Exception e) {
log.error("SQL error at batch UPDATE OR INSERT INTO {}\n{}", getTableName(), ExceptionUtils.getStackTrace(e));
throw e;
}
}
protected Instant getInstantFromTimestamp(ResultSet rs, String column) throws SQLException {
Timestamp date = rs.getTimestamp(column);
return date != null ? date.toInstant() : null;
}
protected Timestamp timestampFromInstant(Instant instant) {
if (instant == null) return null;
return Timestamp.from(instant);
}
}

View file

@ -0,0 +1,48 @@
package ru.spcex.clearing.storage.config;
/**
* Корневой элемент конфигурации
*/
@SuppressWarnings("DefaultAnnotationParam")
public class ConfigurationRootElement /*extends ConfigurationRootBaseElement*/ {
/**
* Конфигурация БД
*/
// @JsonProperty(value = "Database", required = true)
private SettingsElementDatabase database = new SettingsElementDatabase();
// @JsonProperty(value = "Settings", required = false)
private SettingsElement settings = new SettingsElement();
/**
* Конфигурация Hazelcast
*/
// @JsonProperty(value = "HazelcastServer", required = true)
// private HazelcastServerElement hazelcast = new HazelcastServerElement();
/**
* Конфигурация сервисов Core
*/
// @JsonProperty(value = "Services")
private ServicesElement servicesElement = new ServicesElement();
public SettingsElementDatabase getDatabase() {
return database;
}
// public HazelcastServerElement getHazelcast() {
// return hazelcast;
// }
public SettingsElement getSettings() {
return settings;
}
public ServicesElement getServicesElement() {
return servicesElement;
}
}

View file

@ -0,0 +1,72 @@
package ru.spcex.clearing.storage.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import ru.spcex.clearing.storage.error.ModuleInitializeException;
import ru.spcex.clearing.storage.utils.DbDefaultConfig;
import javax.sql.DataSource;
import java.sql.Connection;
@SuppressWarnings("UnnecessaryLocalVariable")
@Configuration
public class DbConnectionConfig {
private Logger log = LoggerFactory.getLogger(this.getClass());
private ConfigurationRootElement configRoot = DfaConfig.get().getRoot();
@Bean
public DataSource dataSource() /*fixme why: throws PropertyVetoException*/ {
DataSource result;
String login = configRoot.getDatabase().getLogin();
String password = configRoot.getDatabase().getPassword();
String logTimeoutPart = "";
String dbPath;
if (StringUtils.isEmpty(configRoot.getDatabase().getEmbeddedFilePath())) {
dbPath = configRoot.getDatabase().getJdbcConnectionString();
int timeoutSec = configRoot.getDatabase().getConnectionAcquireTimeoutSeconds();
ComboPooledDataSource cpds = new ComboPooledDataSource();
try {
cpds.setDriverClass(configRoot.getDatabase().getDriver());
} catch (Exception ue) { // fixme не компилится из-за PropertyVetoException
throw new RuntimeException(ue);
}
cpds.setJdbcUrl(dbPath);
cpds.setUser(login);
cpds.setPassword(password);
cpds.setInitialPoolSize(configRoot.getDatabase().getMinPoolSize());
cpds.setMinPoolSize(configRoot.getDatabase().getMinPoolSize());
cpds.setMaxPoolSize(configRoot.getDatabase().getMaxPoolSize());
cpds.setNumHelperThreads(configRoot.getDatabase().getNumHelperThreads());
cpds.setCheckoutTimeout(timeoutSec * 1000/*todo common 1000==ConstsCommon.SECOND*/);
logTimeoutPart = String.format(" (timeout=%ds)", timeoutSec);
result = cpds;
} else {
dbPath = configRoot.getDatabase().getEmbeddedFilePath();
result = DbDefaultConfig.getEmbeddedDatabase(dbPath);
}
String OPERATION_DATABASE_CONNECTION_CHECK = String.format("Database [%s] connection check", dbPath);
try {
Connection conn = result.getConnection();
conn.close();
log.info("{}: success", OPERATION_DATABASE_CONNECTION_CHECK);
return result;
} catch (Throwable e) {
String msg = String.format("%s%s: failed: %s -> %s",
OPERATION_DATABASE_CONNECTION_CHECK, logTimeoutPart, e.getClass().getSimpleName(), e.getMessage());
log.error(msg);
throw new ModuleInitializeException(msg, e);
}
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate;
}
}

View file

@ -0,0 +1,34 @@
package ru.spcex.clearing.storage.config;
public class DfaConfig {
protected static DfaConfig self;
ConfigurationRootElement root = new ConfigurationRootElement();
protected String defaultLogFileName() {
return "storage";
}
public String appName() {
return "clearing-storage";
}
protected Class parsedClazz() {
return ConfigurationRootElement.class;
}
public static DfaConfig get() {
if (self == null) {
self = new DfaConfig();
}
return self;
}
public ConfigurationRootElement getRoot() {
return root;
}
}

View file

@ -0,0 +1,71 @@
package ru.spcex.clearing.storage.config;
import com.hazelcast.config.*;
import com.hazelcast.core.HazelcastInstance;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.hazelcast.HazelcastInstanceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HazelcastConfiguration {
private ConfigurationRootElement configRoot = DfaConfig.get().getRoot();
private final PoolMapConfigs poolMapConfigs;
@Autowired
public HazelcastConfiguration(PoolMapConfigs poolMapConfigs) {
this.poolMapConfigs = poolMapConfigs;
}
@Bean
public HazelcastInstance hazelcastServerInstance(Config config) {
return (new HazelcastInstanceFactory(config)).getHazelcastInstance();
}
@Bean
public Config hazelCastConfig() {
Config config = new Config();
config.setInstanceName("instance");
config.setGroupConfig(new GroupConfig()
// todo config .setName(configRoot.getHazelcast().getLogin())
// .setPassword(configRoot.getHazelcast().getPassword()
// )
);
// if (StringUtils.isEmpty(configRoot.getHazelcast().getMancenterUrl())) {
// config.setManagementCenterConfig(new ManagementCenterConfig()
// .setEnabled(false)
// );
// } else {
// config.setManagementCenterConfig(new ManagementCenterConfig()
// .setEnabled(true)
// .setUrl(configRoot.getHazelcast().getMancenterUrl())
// );
// }
config.setProperty("hazelcast.shutdownhook.enabled", "true");
config.setProperty("hazelcast.logging.type", "slf4j");
config.setProperty("hazelcast.operation.call.timeout.millis", "600000");
config.setNetworkConfig(new NetworkConfig()
// .setPort(configRoot.getHazelcast().getListenPort())
.setJoin(new JoinConfig()
.setMulticastConfig(new MulticastConfig()
.setEnabled(false))
.setTcpIpConfig(new TcpIpConfig()
.setEnabled(true)
// .setMembers(
// DfaConfig.get()
// .getRoot().getHazelcast().getClusterMembersList()
// )
)
)
);
config.addMapConfig(poolMapConfigs.map_WorkflowStatusDictionary());
config.addMapConfig(poolMapConfigs.map_WorkflowStatusDictionary());
return config;
}
}

View file

@ -0,0 +1,9 @@
package ru.spcex.clearing.storage.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.moex.platform.storage")
public class PlatformStorageBeans {
}

View file

@ -0,0 +1,74 @@
package ru.spcex.clearing.storage.config;
import com.hazelcast.config.*;
import com.hazelcast.core.MapLoader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import ru.spcex.clearing.storage.dictionary.WorkflowStatusDictionaryMapStore;
import ru.spcex.clearing.storage.object.CompanyRoleSetMapStore;
import ru.spcex.clearing.storage.utils.IMDGDistributedNames;
@Configuration
public class PoolMapConfigs {
private ConfigurationRootElement configRoot = DfaConfig.get().getRoot();
public PoolMapConfigs() {
}
private MapStoreConfig makeDefaultMapStoreConfig(MapLoader<Long, ?> mapBean) {
return new MapStoreConfig()
.setImplementation(mapBean)
;//todo config: .setWriteDelaySeconds(configRoot.getHazelcast().getDbSyncSeconds());
}
public ScheduledExecutorConfig makeDefaultScheduledExecutorConfig(String name) {
return new ScheduledExecutorConfig()
.setName(name)
.setPoolSize(16)
.setDurability(1)
.setCapacity(0);
}
public MapConfig makeDefaultMapConfig(String mapName, MapLoader<Long, ?> mapBean) {
return new MapConfig()
.setName(mapName)
.setInMemoryFormat(InMemoryFormat.OBJECT)
.setMapStoreConfig(makeDefaultMapStoreConfig(mapBean))
//fixme config: .setBackupCount(configRoot.getHazelcast().getBackupCount())
;
}
private NearCacheConfig makeDefaultNearCacheConfig() {
return new NearCacheConfig()
.setMaxIdleSeconds(3600)
.setInMemoryFormat(InMemoryFormat.OBJECT)
.setSerializeKeys(true)
;
}
private MapIndexConfig makeMapIndexConfig(String attributeName, boolean ordered) {
return new MapIndexConfig()
.setAttribute(attributeName)
.setOrdered(ordered);
}
@Autowired
private WorkflowStatusDictionaryMapStore workflowStatusDictionaryMapStore;
public MapConfig map_WorkflowStatusDictionary() {
return makeDefaultMapConfig(IMDGDistributedNames.Map_WorkflowStatusDictionary, workflowStatusDictionaryMapStore)
.setNearCacheConfig(makeDefaultNearCacheConfig());
}
@Autowired
private CompanyRoleSetMapStore companyRoleSetMapStore;
public MapConfig map_CompanyRoleSet() {
return makeDefaultMapConfig(IMDGDistributedNames.Map_CompanyRoleSet, companyRoleSetMapStore);
}
}

View file

@ -0,0 +1,34 @@
package ru.spcex.clearing.storage.config;
//import com.fasterxml.jackson.annotation.JsonFormat;
//import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Date;
/**
* Настройка сервисов
*/
public class ServicesElement implements Serializable {
/**
* Время, через которое выставленная заявка будет снята, в миллисекундах.
*/
// @JsonProperty(value = "ExpirationDelay")
private Integer ExpirationDelay = 5 * 60 * 1000;
public Integer getExpirationDelay() {
return ExpirationDelay;
}
/**
* Время окончания торговой сессии
*/
// @JsonProperty(value = "SessionEndTime")
// @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss", timezone = "Europe/Moscow")
private Date SessionEndTime = null;
public Date getSessionEndTime() {
return SessionEndTime;
}
}

View file

@ -0,0 +1,29 @@
package ru.spcex.clearing.storage.config;
//import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
@SuppressWarnings({"DefaultAnnotationParam", "unused", "FieldCanBeLocal"})
public class SettingsElement implements Serializable {
// @JsonProperty(value = "InitHazelcastThreadMultiplier", required = false)
private int initHazelcastThreadMultiplier = 2;
// @JsonProperty(value = "RecoveryLogPath", required = false)
private String recoveryLogPath;
// @JsonProperty(value = "ExecutionLimitationPeriod", required = false)
private Integer executionLimitationPeriod = 10;
public int getInitHazelcastThreadMultiplier() {
return initHazelcastThreadMultiplier;
}
public Integer getExecutionLimitationPeriod() {
return executionLimitationPeriod;
}
public String getRecoveryLogPath() {
return recoveryLogPath;
}
}

View file

@ -0,0 +1,89 @@
package ru.spcex.clearing.storage.config;
//import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
@SuppressWarnings({"FieldCanBeLocal", "unused", "DefaultAnnotationParam"})
public class SettingsElementDatabase implements Serializable {
// @JsonProperty(value = "Driver")
private String driver = "org.firebirdsql.jdbc.FBDriver";
// @JsonProperty(value = "JdbcConnectionString", required = true)
private String jdbcConnectionString;
// @JsonProperty(value = "Login", required = true)
private String login;
// @JsonProperty(value = "Password", required = true)
private String password;
// @JsonProperty(value = "MaxPoolSize")
private int maxPoolSize = 30;
// @JsonProperty(value = "MinPoolSize")
private int minPoolSize = 10;
// @JsonProperty(value = "EmbeddedFilePath", required = false)
private String embeddedFilePath;
// @JsonProperty(value = "NumHelperThreads", required = false)
private int numHelperThreads = Runtime.getRuntime().availableProcessors() * 2;
// @JsonProperty(value = "ConnectionAcquireTimeoutSeconds", required = false)
private int connectionAcquireTimeoutSeconds = 30;
public String getDriver() {
return driver;
}
public String getJdbcConnectionString() {
return jdbcConnectionString;
}
public String getLogin() {
return login;
}
public String getPassword() {
return password;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public int getMinPoolSize() {
return minPoolSize;
}
public String getEmbeddedFilePath() {
return embeddedFilePath;
}
public int getNumHelperThreads() {
return numHelperThreads;
}
public int getConnectionAcquireTimeoutSeconds() {
return connectionAcquireTimeoutSeconds;
}
// для поддержки интеграционных тестов otc-test:
public void setJdbcConnectionString(String jdbcConnectionString) {
this.jdbcConnectionString = jdbcConnectionString;
}
public void setLogin(String login) {
this.login = login;
}
public void setPassword(String password) {
this.password = password;
}
public void setEmbeddedFilePath(String embeddedFilePath) {
this.embeddedFilePath = embeddedFilePath;
}
}

View file

@ -0,0 +1,24 @@
package ru.spcex.clearing.storage.dictionary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import ru.clearing.platform.dictionary.WorkflowStatusDictionary;
import ru.spcex.clearing.storage.base.DictionaryMapStore;
@Component
public class WorkflowStatusDictionaryMapStore extends DictionaryMapStore<WorkflowStatusDictionary> {
public WorkflowStatusDictionaryMapStore(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
@Override
public String getTableName() {
return "WORKFLOW_STATUS_DICTIONARY";
}
@Override
public WorkflowStatusDictionary getDictionaryObject() {
return new WorkflowStatusDictionary();
}
}

View file

@ -0,0 +1,19 @@
package ru.spcex.clearing.storage.error;
public class ModuleInitializeException extends RuntimeException {
public ModuleInitializeException() {
}
public ModuleInitializeException(String message) {
super(message);
}
public ModuleInitializeException(String message, Throwable cause) {
super(message, cause);
}
public ModuleInitializeException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,61 @@
package ru.spcex.clearing.storage.object;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import ru.clearing.classes.StaticData.Company.Company;
import ru.clearing.classes.StaticData.Company.CompanyRoleSet;
import ru.spcex.clearing.storage.base.ObjectBaseMapStore;
import ru.spcex.clearing.storage.utils.DbUtilsHelper;
import java.util.*;
@Component
public class CompanyRoleSetMapStore extends ObjectBaseMapStore<CompanyRoleSet> {
public CompanyRoleSetMapStore(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
@Override
public String getTableName() {
return "COMPANY_ROLE_SET";
}
@Override
public String[] getFields() {
return new String[]{"id", "roleid", "companyid"};
}
protected final String insertStatement = DbUtilsHelper.createUpdateOrInsert(getTableName(), getFields(), "id");
@Override
public Collection<CompanyRoleSet> load(Collection<Long> keys) {
Map<String, Collection<Long>> paramMap = Collections.singletonMap("ids", keys);
return namedParameterJdbcTemplate.query("select * from " + getTableName() + " where id in (:ids)", paramMap,
(resultSet, i) -> {
CompanyRoleSet companyRoleSet = new CompanyRoleSet();
companyRoleSet.setId(resultSet.getObject("id", Long.class));
companyRoleSet.setRoleId(resultSet.getObject("roleid", Long.class));
companyRoleSet.setCompanyId(resultSet.getObject("companyid", Long.class));
return companyRoleSet;
});
}
@Override
public void store(Map<Long, CompanyRoleSet> map) {
List<Object[]> batchArgs = new ArrayList<>();
for (Map.Entry<Long, CompanyRoleSet> entry : map.entrySet()) {
CompanyRoleSet partnerList = entry.getValue();
Object[] args = new Object[]{
partnerList.getId(),
partnerList.getRoleId(),
partnerList.getCompanyId(),
};
batchArgs.add(args);
}
batchInsertUpdate(insertStatement, batchArgs);
}
}

View file

@ -0,0 +1,111 @@
package ru.spcex.clearing.storage.utils;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
public class BigDecimalUtil {
private static final DecimalFormat defaultDecimalFormat;
static {
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols();
decimalFormatSymbols.setDecimalSeparator('.');
decimalFormatSymbols.setGroupingSeparator(' ');
defaultDecimalFormat = new DecimalFormat("#,###.00", decimalFormatSymbols);
}
public static BigDecimal sum(BigDecimal num1, BigDecimal num2) {
BigDecimal sum = BigDecimal.ZERO;
if (num1 != null)
sum = sum.add(num1);
if (num2 != null)
sum = sum.add(num2);
return sum;
}
/**
* @param value
* @param dbSize общее кол-во позиций
* @param dbScale кол-во позиций под дробную часть
* @return true, если размер целой части не превышает значения, указанные в параметрах
*/
public static boolean checkSize(BigDecimal value, int dbSize, int dbScale) {
if (value == null)
return true;
return value.precision() - value.scale() <= dbSize - dbScale;
}
public static BigDecimal genMaxValueForInsert(int dbSize, int dbScale) {
int maxIntSizeForInsert = dbSize - dbScale;
long newLongValue = new BigDecimal(Math.pow(10, maxIntSizeForInsert)).longValue();
return new BigDecimal(newLongValue - 1);
}
public static BigDecimal chooseHighest(BigDecimal oldPrice, BigDecimal newPrice) {
if (newPrice != null && (oldPrice == null || isLess(oldPrice, newPrice))) {
return newPrice;
} else {
return oldPrice;
}
}
public static BigDecimal chooseSmallest(BigDecimal oldPrice, BigDecimal newPrice) {
if (newPrice != null && (oldPrice == null || isLess(newPrice, oldPrice))) {
return newPrice;
} else {
return oldPrice;
}
}
/**
* Если bestPrice меньше price, то true, иначе false
*
* @param bestPrice
* @param price
* @return
*/
public static boolean isLess(BigDecimal bestPrice, BigDecimal price) {
return price != null && bestPrice != null && bestPrice.compareTo(price) < 0;
}
public static Integer nullSafeAdd(Integer a, Integer b) {
if (a == null) {
return b;
} else if (b == null) {
return a;
} else {
return a + b;
}
}
public static BigDecimal nullSafeAdd(BigDecimal a, BigDecimal b) {
if (a == null) {
return b;
} else if (b == null) {
return a;
} else {
return a.add(b);
}
}
public static Long nullSafeAdd(Long a, Long b) {
if (a == null) {
return b;
} else if (b == null) {
return a;
} else {
return a + b;
}
}
public static String defaultFormatValue(BigDecimal val) {
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols();
decimalFormatSymbols.setDecimalSeparator('.');
decimalFormatSymbols.setGroupingSeparator(' ');
if (val == null) {
return "";
}
return defaultDecimalFormat.format(val);
}
}

View file

@ -0,0 +1,11 @@
package ru.spcex.clearing.storage.utils;
import javax.sql.DataSource;
import java.nio.file.Paths;
public class DbDefaultConfig {
public static DataSource getEmbeddedDatabase(String embeddedFilePath) {
throw new UnsupportedOperationException("Are PostgreSQL not supported embedded DB?");//todo PsotgreSQL
}
}

View file

@ -0,0 +1,51 @@
package ru.spcex.clearing.storage.utils;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class DbUtilsHelper {
private static String generateValuesBlock(int querySignCount) {
return StringUtils.rightPad("(?", querySignCount * 3 - 1, ", ?") + ")";
}
private static Pattern pGetColumns = Pattern.compile("(?m)(?i)INTO\\s+[0-9a-z\" _$]+\\s+\\(\\s*([0-9a-z_\\- ,\"\\n+]*?)\\s*\\)");
public static String fillValuesBlock(String insertStatement) {
Matcher m = pGetColumns.matcher(insertStatement);
if (m.find()) {
String g = m.group(1);
String block = generateValuesBlock(g.split(",").length);
return insertStatement.replaceAll("%values_block%", block);
} else {
return insertStatement;
}
}
public static String createInsert(String tableName, String[] fields) {
return "INSERT INTO " + tableName +
" (" + Arrays.stream(fields).map(f -> "\"" + f.toUpperCase() + "\"").collect(Collectors.joining(", ")) + ") values (" +
// Arrays.stream(fields).map(f -> "\"" + f.toUpperCase() + "\"").collect(Collectors.joining(", ", ":", ""));
String.join(", ", Collections.nCopies(fields.length, "?")) +
")";
}
public static String createUpdateOrInsert(String tableName, String[] fields, String matchingKey) {
String updateOrInsert = "INSERT INTO " + tableName +
" (" + Arrays.stream(fields).map(f -> "\"" + f.toUpperCase() + "\"").collect(Collectors.joining(", ")) + ") values (" +
// Arrays.stream(fields).map(f -> "\"" + f.toUpperCase() + "\"").collect(Collectors.joining(", ", ":", ""));
String.join(", ", Collections.nCopies(fields.length, "?")) +
") WHERE ";
if (matchingKey != null) // PostgreSQL conflict_target fixme проверить мэтчинг
updateOrInsert += " " + matchingKey.toUpperCase();
updateOrInsert += " CONFLICT DO UPDATE";
return updateOrInsert;
}
}

View file

@ -0,0 +1,10 @@
package ru.spcex.clearing.storage.utils;
//fixme перенести в новый модуль IMDGDistributedNames
public final class IMDGDistributedNames {
private IMDGDistributedNames() {
}
public static final String Map_WorkflowStatusDictionary = "Map_WorkflowStatusDictionary";
public static final String Map_CompanyRoleSet = "Map_CompanyRoleSet";
}

View file

@ -0,0 +1,288 @@
package ru.spcex.clearing.storage.utils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
/**
* Для работы с java.util.Date
* И конвертация в новый формат
* todo копипаст юниттеста нужен.
*/
public class TimeUtil {
private static final Logger log = LoggerFactory.getLogger(TimeUtil.class);
public static final ZoneId zone = ZoneId.systemDefault();
/**
* Получить дату без времени (начало дня)
*
* @param date
* @return
*/
public static Date getDateOnStartOfDay(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
/**
* Получить начало следующего дня
*
* @param date
* @return
*/
public static Date getDateNextDay(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.add(Calendar.DAY_OF_YEAR, 1);
return calendar.getTime();
}
public static LocalDateTime toDateTime(Date fromDate) {
if (fromDate == null)
return null;
if (fromDate instanceof java.sql.Timestamp)
return ((java.sql.Timestamp) fromDate).toLocalDateTime();
if (fromDate instanceof java.sql.Date || fromDate instanceof java.sql.Time)
log.debug("Warning at converting date/time: for class {} may not contain date or time. unixtime={}", fromDate.getClass(), fromDate.getTime());
return toDateTime0(fromDate);
}
private static LocalDateTime toDateTime0(Date fromDate) {
try {
if (fromDate instanceof java.sql.Date) {
Instant instant = Instant.now(); //can be LocalDateTime
ZoneId systemZone = ZoneId.systemDefault(); // my timezone
ZoneOffset currentOffsetForMyZone = systemZone.getRules().getOffset(instant);
return LocalDateTime.ofEpochSecond(fromDate.getTime() / 1000, 0, currentOffsetForMyZone);
}
return LocalDateTime.ofInstant(fromDate.toInstant(), ZoneId.systemDefault());
} catch (UnsupportedOperationException ue) {
log.warn("Error(warn) convert type of date/time: {}({}) class: {}. Retry", fromDate, fromDate.getTime(), fromDate.getClass());
try {
Instant instant = Instant.now(); //can be LocalDateTime
ZoneId systemZone = ZoneId.systemDefault(); // my timezone
ZoneOffset currentOffsetForMyZone = systemZone.getRules().getOffset(instant);
return LocalDateTime.ofEpochSecond(fromDate.getTime() / 1000, 0, currentOffsetForMyZone);
} catch (Exception e) {
log.error("Error convert type2 of date/time: {} class: {}\n{}", fromDate, fromDate.getClass(), ExceptionUtils.getStackTrace(ue));
return null;
}
}
// Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
}
public static LocalDate toDate(Date fromDate) {
if (fromDate == null)
return null;
if (fromDate instanceof java.sql.Date)
return ((java.sql.Date) fromDate).toLocalDate();
if (fromDate instanceof java.sql.Time)
log.debug("Warning at converting date/time: for class {} may not contain date. unixtime={}", fromDate.getClass(), fromDate.getTime());
return toDateTime0(fromDate).toLocalDate();
}
public static LocalTime toTime(Date fromDate) {
if (fromDate == null)
return null;
if (fromDate instanceof java.sql.Time)
return ((java.sql.Time) fromDate).toLocalTime();
if (fromDate instanceof java.sql.Date)
log.debug("Warning at converting date/time: for class {} may not contain time. unixtime={}", fromDate.getClass(), fromDate.getTime());
return toDateTime0(fromDate).toLocalTime();
}
/**
* Последовательный ряд дат дней между двумя датами.
*
* @param from с даты, включительно
* @param to по дату, включительно.
* @return
*/
public static Date[] makeDateRange(Date from, Date to) {
ArrayList<Date> datesL = new ArrayList<Date>();
LocalDate fromDate = toDate(from);
LocalDate toDate = toDate(to);
datesL.add(from);
for (LocalDate dateI = fromDate.plusDays(1); dateI.isBefore(toDate); dateI = dateI.plusDays(1)) {
Date date = Date.from(dateI.atStartOfDay(ZoneId.systemDefault()).toInstant());
datesL.add(date);
}
if (!fromDate.equals(toDate))
datesL.add(to);
Date[] dates = datesL.toArray(new Date[0]);
return dates;
}
public static Date fromInstant(Instant instant) {
if (instant == null) return null;
return Date.from(instant);
}
public static Date from(LocalDateTime localDateTime) {
return localDateTime != null ? Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()) : null;
}
public static Date from(LocalDate localDate) {
return localDate != null ? Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()) : null;
}
/**
* иногда встречается java.sql.Date
* у них toInstant() не поддерживается
*/
public static Instant fromDate(Date dateToConvert) {
try {
if (dateToConvert == null) return null;
return dateToConvert.toInstant();
} catch (UnsupportedOperationException e) {
// log.warn("java.sql.Date");
if (dateToConvert instanceof java.sql.Date) {
return ((java.sql.Date) dateToConvert).toLocalDate().atStartOfDay(ZoneId.systemDefault()).toInstant();
} else {
throw e;
}
}
}
public static Instant fromDate(LocalDate dateToConvert) {
return dateToConvert == null ? null : dateToConvert.atStartOfDay(ZoneId.systemDefault()).toInstant();
}
public static LocalDateTime toDateTime(Instant instant) {
if (instant == null) {
return null;
} else {
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
}
public static Date toUtilDate(java.sql.Date sqlDate) {
return sqlDate != null ? new Date(sqlDate.getTime()) : null;
}
public static boolean isCurrentTradingDay(Date date) {
Date tradingDay = getDateOnStartOfDay(date);
Date currentDay = getDateOnStartOfDay(new Date());
return currentDay.equals(tradingDay);
}
/**
* Начало дня по текущей временной зоне
*/
public static Instant startOfDayNow() {
ZonedDateTime zdtStart = ZonedDateTime.now();
zdtStart = zdtStart.truncatedTo(ChronoUnit.DAYS);
return zdtStart.toInstant();
}
/**
* Начало дня по текущей временной зоне.
*
* @param from
* @return
*/
public static Instant startOfDay(Instant from) {
ZonedDateTime zdtStart = from.atZone(ZoneId.systemDefault());
zdtStart = zdtStart.truncatedTo(ChronoUnit.DAYS);
return zdtStart.toInstant();
}
public static ZonedDateTime toZoned(Instant instant) {
if (instant == null) return null;
return instant.atZone(zone);
}
public static LocalDate toDate(Instant instant) {
ZonedDateTime zoned = toZoned(instant);
if (zoned == null) return null;
return zoned.toLocalDate();
}
/**
* Сегодня (от начала дня) или позднее
*
* @param instant дата, может ыть null
* @return
*/
public static boolean afterToday(Instant instant) {
LocalDate localDate = toDate(instant);
return afterToday(localDate);
}
/**
* Сегодня (от начала дня) или позднее
*
* @param date дата, может ыть null
* @return
*/
public static boolean afterToday(Date date) {
LocalDate localDate = toDate(date);
return afterToday(localDate);
}
/**
* Сегодня (от начала дня) или позднее
*
* @param localDate дата, может ыть null
* @return
*/
public static boolean afterToday(LocalDate localDate) {
if (localDate == null) return false;
return !localDate.isBefore(LocalDate.now());
}
public static boolean isToday(Instant instant) {
LocalDate localDate = toDate(instant);
return isToday(localDate);
}
public static boolean isToday(Date date) {
LocalDate localDate = toDate(date);
return isToday(localDate);
}
public static boolean isToday(LocalDate localDate) {
if (localDate == null) return false;
return localDate.equals(LocalDate.now());
}
/**
* Если instant без даты (1970 год), то берёт из него время и добавляет текущую дату.
*
* @param time может быть null
* @return time, или если не указана дата, то текущая дата + время из <code>time</code>
*/
public static Instant addDateTodayToTimeIfNeeded(Instant time) {
if (time == null)
return null;
LocalDateTime ldt = TimeUtil.toDateTime(time);
if (ldt.getYear() == 1970 && ldt.getMonth() == Month.JANUARY && ldt.getDayOfMonth() == 1) {
// time without date
ldt = LocalDateTime.of(LocalDate.now(), ldt.toLocalTime());
return ldt.atZone(ZoneId.systemDefault()).toInstant();
}
return time;
}
public static String formatInstantToString(DateTimeFormatter formatter, Instant date) {
if (formatter == null || date == null) return "";
return formatter.format(date);
}
}