Контракты#
Контракты — это декларации интерфейсов, которые используются для разрыва бинарной зависимости между модулями. Вместо прямой зависимости от реализации модуля A модуль B зависит только от контрактов модуля A. Это позволяет изменять реализацию модуля A без перекомпиляции модуля B.
Контракты содержат все методы из Dpi и аннотированные программистом методы из Api.

Адаптеры#
Адаптеры — связующее звено, которое позволяет контрактам работать с Api, Pkg, Lib. Генерируются вместе с контрактами и имеют следующие расширения: Apa, Pka, Lia.
Аннотации#
Для декларации контрактов используются аннотации:
ExternalApi— для апи;ExternalLib— для библиотек;ExternalPkg— для пакетов.
Аннотации для методов:
CntInclude— сообщает, что для этого метода необходимо сгенерировать контракт;CntExclude— сообщает, что для этого метода не нужно генерировать контракт.
Аннотация декларации контрактов имеет правило defaultRule, от которого зависит генерация методов в контракте (значение по умолчанию Include):
MethodScope.Include— контракты генерируются для всех публичных методов, которые не отмеченыCntExclude;MethodScope.Exclude— контракты генерируются только для публичных методов, которые отмечены аннотациейCntInclude.
Пример декларации контрактов:
@ExternalApi(defaultRule = MethodScope.Include)
class Btk_FileApi {
// Приватный метод не будет сгенерирован в любом сценарии
private def doSomething(): Unit = {
}
// Не будет сгенерирован из-за аннотации
@CntExclude
def withFile(fileStorage: FileStorage, fullFileName: NString)(f: File => Unit): Unit = {
val fileSrc = fileStorage.FileFactory(fullFileName)
if (fileSrc.exists()) {
f(fileSrc)
} else {
throw AppException(s"Файл $fullFileName был перемещен или удален из хранилища")
}
}
// Будет сгенерирован из-за аннотации
@CntInclude
def getFileStorageByFile(idpFile: NLong): NString = {
if (idpFile.isNotNull) {
val ropFile = Btk_FileApi().load(idpFile)
val idFileStorage = ropFile.get(_.idFileStorage)
Btk_FileStorageApi().getMnemoCode(idFileStorage).getOrElse("Default")
} else {
throw AppException("Не передан идентификатор файла")
}
}
}
Генерация контрактов#
После проставления аннотаций запустите генерацию. Процесс выполняется аналогично генерации исходного кода (source generator). Для этого необходимо:
Установить новую версию плагина для инструкции по установке плагина для IDEA и обновить
external-toolsна главном экране IDEA;Включить флаг
shouldGenerateContract: trueв конфигурации пакетов сборки модуля;Проставить аннотации на
Api,Pkg,Lib;Сгенерировать контракты по необходимым файлам;
Сгенерировать по этим же файлам исходный код.
Результат генерации по Api#
Пример сгенерированного контракта Btk_FileApc:
Внимание
Все методы, объявленные только в Dpi, автоматически попадают в контракт, их не нужно аннотировать.
trait Btk_FileDpc[
ARC <: Btk_FileDrc,
ATC <: Btk_FileDtc[ARC, Api[java.lang.Long, ARC], ATC],
APC <: Btk_FileApc
] extends RootApc[java.lang.Long, ARC, APC]
with JIdExtApc[EntityAbst, java.lang.Long, ARC]
with AttrApc[java.lang.Long, ARC]
trait Btk_FileApc extends Btk_FileDpc[Btk_FileDrc, Btk_FileAtc, Btk_FileApc] {
def copyObject(idFrom: NLong, idTo: NLong, idParent: NLong, params: Btk_CopyObjectParam): NLong
def copyObject(idFrom: NLong, idTo: NLong, idParent: NLong): NLong
def setgidSrc(rop: ApiRop, value: NGid): Unit
def setsFileName(rop: ApiRop, value: NString): Unit
def setsExt(rop: ApiRop, value: NString): Unit
def setsFullFileName(rop: ApiRop, value: NString): Unit
def setidFileStorage(rop: ApiRop, value: NLong): Unit
def setsSHA2(rop: ApiRop, value: NString): Unit
def setsMD5(rop: ApiRop, value: NString): Unit
def setnSize(rop: ApiRop, value: NNumber): Unit
def setidAttachType(rop: ApiRop, value: NLong): Unit
def setidSrcObjectType(rop: ApiRop, value: NLong): Unit
def setbManualMoved(rop: ApiRop, value: NNumber): Unit
def setbDeleted(rop: ApiRop, value: NNumber): Unit
def setjParams(rop: ApiRop, value: NString): Unit
def parsejParams(rop: ApiRop): JESegment
def getFileStorageByFile(idpFile: NLong): NString
}
object Btk_FileApc extends ApcFactory[Btk_FileDrc, Btk_FileApc] {
override protected val sApiClassPath: String = "ru.bitec.app.btk.Btk_FileApi$"
override protected val sApaClassPath: String = "ru.bitec.app.btk.Btk_FileApa"
}
trait Btk_FileAtc extends Btk_FileDtc[Btk_FileDrc, Api[java.lang.Long, Btk_FileDrc], Btk_FileAtc]
object Btk_FileAtc extends AtcFactory[java.lang.Long, Btk_FileDrc, Api[java.lang.Long, Btk_FileDrc], Btk_FileAtc] {
override protected val sAtaClassPath: String = "ru.bitec.app.btk.Btk_FileAta$"
}
trait Btk_FileDrc extends SEntityArc[java.lang.Long] {
def id: NLong
def id_=(value: NLong): Unit
def idClass: NLong
def idClass_=(value: NLong): Unit
def gid: NGid
def gid_=(value: NGid): Unit
def gidSrc: NGid
def gidSrc_=(value: NGid): Unit
def sFileName: NString
def sFileName_=(value: NString): Unit
def sExt: NString
def sExt_=(value: NString): Unit
def sFullFileName: NString
def sFullFileName_=(value: NString): Unit
def idFileStorage: NLong
def idFileStorage_=(value: NLong): Unit
def sSHA2: NString
def sSHA2_=(value: NString): Unit
def sMD5: NString
def sMD5_=(value: NString): Unit
def nSize: NNumber
def nSize_=(value: NNumber): Unit
def idAttachType: NLong
def idAttachType_=(value: NLong): Unit
def idSrcObjectType: NLong
def idSrcObjectType_=(value: NLong): Unit
def bManualMoved: NNumber
def bManualMoved_=(value: NNumber): Unit
def bDeleted: NNumber
def bDeleted_=(value: NNumber): Unit
def jParams: NString
def jParams_=(value: NString): Unit
}
trait Btk_FileDtc[
ARO <: Arc[java.lang.Long],
API <: Api[java.lang.Long, ARO],
ATA <: BaseAtaImpl[java.lang.Long, ARO, API]
] extends BaseAta[java.lang.Long, ARO, API, ATA] {
val id: Column
val idClass: Column
val gid: Column
val gidSrc: Column
val sFileName: Column
val sExt: Column
val sFullFileName: Column
val idFileStorage: Column
val sSHA2: Column
val sMD5: Column
val nSize: Column
val idAttachType: Column
val idSrcObjectType: Column
val bManualMoved: Column
val bDeleted: Column
val jParams: Column
}
Пример сгенерированного адаптера Btk_FileApa:
class Btk_FileApa extends Btk_FileApc
with RootApa[java.lang.Long, Btk_FileDrc, Btk_FileApc]
with JIdExtApa[EntityAbst, java.lang.Long, Btk_FileDrc]
with AttrApa[java.lang.Long, Btk_FileDrc] {
private type ApiType = Btk_FileApi
private def apiInstance: ApiType = _apiInstance.asInstanceOf[ApiType]
override def copyObject(idFrom: NLong, idTo: NLong, idParent: NLong, params: Btk_CopyObjectParam): NLong = {
apiInstance.copyObject(idFrom, idTo, idParent, params)
}
override def copyObject(idFrom: NLong, idTo: NLong, idParent: NLong): NLong = {
apiInstance.copyObject(idFrom, idTo, idParent)
}
override def setgidSrc(rop: ApiRop, value: NGid): Unit = {
apiInstance.setgidSrc(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setsFileName(rop: ApiRop, value: NString): Unit = {
apiInstance.setsFileName(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setsExt(rop: ApiRop, value: NString): Unit = {
apiInstance.setsExt(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setsFullFileName(rop: ApiRop, value: NString): Unit = {
apiInstance.setsFullFileName(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setidFileStorage(rop: ApiRop, value: NLong): Unit = {
apiInstance.setidFileStorage(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setsSHA2(rop: ApiRop, value: NString): Unit = {
apiInstance.setsSHA2(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setsMD5(rop: ApiRop, value: NString): Unit = {
apiInstance.setsMD5(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setnSize(rop: ApiRop, value: NNumber): Unit = {
apiInstance.setnSize(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setidAttachType(rop: ApiRop, value: NLong): Unit = {
apiInstance.setidAttachType(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setidSrcObjectType(rop: ApiRop, value: NLong): Unit = {
apiInstance.setidSrcObjectType(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setbManualMoved(rop: ApiRop, value: NNumber): Unit = {
apiInstance.setbManualMoved(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setbDeleted(rop: ApiRop, value: NNumber): Unit = {
apiInstance.setbDeleted(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def setjParams(rop: ApiRop, value: NString): Unit = {
apiInstance.setjParams(rop.asInstanceOf[ApiType#ApiRop], value)
}
override def parsejParams(rop: ApiRop): JESegment = {
apiInstance.parsejParams(rop.asInstanceOf[ApiType#ApiRop])
}
override def getFileStorageByFile(idpFile: NLong): NString = {
apiInstance.getFileStorageByFile(idpFile)
}
}
Использование в прикладном коде осуществляется точно так же, как с обычным Api:
val rop = Btk_FileApc().load(123.nl)
val sFileName = rop.get(_.sFileName)
Btk_FileApc().setidAttachType(rop, Btk_AttachTypeApc().getDefault())
Результат генерации по Lib#
Пример сгенерированного контракта Btk_FileLic:
trait Btk_FileLic extends Lic {
/** Имя переменной выборки для контекста подбора файлового хранилища */
def uploadFile(idpFileStorage: NLong, gidpSrc: NGid, bpManualMoved: NNumber, idpAttachFileType: NLong, idpSrcObjectType: NLong, idpClassDoc: NLong, bpTemp: NNumber): NLong
/** Имя переменной выборки для контекста подбора файлового хранилища */
def uploadFiles(idpFileStorage: NLong, gidpSrc: NGid, bpManualMoved: NNumber, idpAttachFileType: NLong, idpSrcObjectType: NLong, idpClassDoc: NLong, bpTemp: NNumber): List[NLong]
}
object Btk_FileLic extends AviLicFactory[Btk_FileLic] {
override protected val sLibClassPath: String = "ru.bitec.app.btk.Btk_FileLib$"
override protected val sLiaClassPath: String = "ru.bitec.app.btk.Btk_FileLia"
}
Пример сгенерированного адаптера Btk_FileLia:
class Btk_FileLia extends Btk_FileLic {
private type LibType = Btk_FileLib
private def libInstance: LibType = _libInstance.asInstanceOf[LibType]
override def uploadFile(idpFileStorage: NLong, gidpSrc: NGid, bpManualMoved: NNumber, idpAttachFileType: NLong, idpSrcObjectType: NLong, idpClassDoc: NLong, bpTemp: NNumber): NLong = {
libInstance.uploadFile(idpFileStorage, gidpSrc, bpManualMoved, idpAttachFileType, idpSrcObjectType, idpClassDoc, bpTemp)
}
override def uploadFiles(idpFileStorage: NLong, gidpSrc: NGid, bpManualMoved: NNumber, idpAttachFileType: NLong, idpSrcObjectType: NLong, idpClassDoc: NLong, bpTemp: NNumber): List[NLong] = {
libInstance.uploadFiles(idpFileStorage, gidpSrc, bpManualMoved, idpAttachFileType, idpSrcObjectType, idpClassDoc, bpTemp)
}
}
Использование в прикладном коде осуществляется точно так же, как с обычной Lib:
Btk_FileLic().uploadFile(...)
Результат генерации по Pkg#
Пример сгенерированного контракта Btk_FilePkc:
trait Btk_FilePkc extends Pkc {
/**
* Проверка наличия файла в хранилище
* @param idpFile id файла
* @return boolean
*/
def checkExist(idpFile: NLong): Boolean
}
object Btk_FilePkc extends PkcFactory[Btk_FilePkc] {
override protected val sPkgClassPath: String = "ru.bitec.app.btk.Btk_FilePkg$"
override protected val sPkaClassPath: String = "ru.bitec.app.btk.Btk_FilePka"
}
Пример сгенерированного адаптера Btk_FilePka:
class Btk_FilePka extends Btk_FilePkc {
private type PkgType = Btk_FilePkg
private def pkgInstance: PkgType = _pkgInstance.asInstanceOf[PkgType]
override def checkExist(idpFile: NLong): Boolean = {
pkgInstance.checkExist(idpFile)
}
}
Использование в прикладном коде осуществляется точно так же, как с обычным Pkg:
new OQuery(Btk_FileAtc.Type) {
where(t.sExt === "png".ns)
}.foreach { rop =>
Btk_FilePkc().checkExist(rop.get(_.id))
}