All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.jetbrains.teamsys.dnq.database.TransientEntityStoreImpl.kt Maven / Gradle / Ivy
/**
* Copyright 2006 - 2022 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.teamsys.dnq.database
import jetbrains.exodus.core.dataStructures.StablePriorityQueue
import jetbrains.exodus.database.*
import jetbrains.exodus.entitystore.*
import jetbrains.exodus.env.EnvironmentConfig
import jetbrains.exodus.env.EnvironmentImpl
import jetbrains.exodus.query.QueryEngine
import jetbrains.exodus.query.metadata.ModelMetaData
import mu.KLogging
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.locks.ReentrantLock
/**
* @author Vadim.Gurov
*/
open class TransientEntityStoreImpl : TransientEntityStore {
companion object : KLogging()
private lateinit var _persistentStore: PersistentEntityStore
var entityLifecycle: EntityLifecycle? = null
/**
* Must be injected.
*/
override var persistentStore: PersistentEntityStore
get() = _persistentStore
set(persistentStore) {
val ec = persistentStore.environment.environmentConfig
if (ec.envTxnDowngradeAfterFlush == EnvironmentConfig.DEFAULT.envTxnDowngradeAfterFlush) {
ec.envTxnDowngradeAfterFlush = false
}
ec.envTxnReplayMaxCount = Integer.MAX_VALUE
ec.envTxnReplayTimeout = java.lang.Long.MAX_VALUE
ec.gcUseExclusiveTransaction = true
_persistentStore = persistentStore
}
/**
* Must be injected.
*/
override lateinit var queryEngine: QueryEngine
override var modelMetaData: ModelMetaData? = null
override var changesMultiplexer: ITransientChangesMultiplexer? = null
set(value) {
field = value
if (value is TransientStoreSessionListener) {
addListener(value, Int.MAX_VALUE)
}
}
override var invocationTransport: ListenerInvocationTransport? = null
private val sessions = Collections.newSetFromMap(ConcurrentHashMap(200))
private val currentSession = ThreadLocal()
private val listeners = StablePriorityQueue()
@field:Volatile
override var isOpen = true
protected set
private var closed = false
private val enumCache = ConcurrentHashMap()
// fair flushLock
internal val flushLock = ReentrantLock(true)
/**
* It's guaranteed that current thread session is Open, if exists
*/
override val threadSession: TransientStoreSession?
get() = currentSession.get()
private val transientSessionOrThrow: TransientSessionImpl
get() = threadSessionOrThrow as TransientSessionImpl
override fun getName() = persistentStore.name
override fun getLocation(): String = persistentStore.location
override fun transactional(
readonly: Boolean,
queryCancellingPolicy: QueryCancellingPolicy?,
isNew: Boolean,
block: (TransientStoreSession) -> T
): T = TransientEntityStoreExt.transactional(this, readonly, queryCancellingPolicy, isNew, block)
override fun beginTransaction(): StoreTransaction {
throw UnsupportedOperationException()
}
override fun beginExclusiveTransaction(): StoreTransaction {
throw UnsupportedOperationException()
}
override fun beginReadonlyTransaction(): TransientStoreSession {
return registerStoreSession(TransientSessionImpl(this, readonly = true))
}
override fun getCurrentTransaction(): StoreTransaction? {
throw UnsupportedOperationException()
}
override fun beginSession(): TransientStoreSession {
assertOpen()
logger.debug { "Begin new session" }
val currentSession = this.currentSession.get()
if (currentSession != null) {
logger.debug { "Return session already associated with the current thread $currentSession" }
return currentSession
}
return registerStoreSession(TransientSessionImpl(this, readonly = false))
}
override fun resumeSession(session: TransientStoreSession?) {
if (session != null) {
assertOpen()
val current = currentSession.get()
if (current != null && current != session) {
throw IllegalStateException("Another open transient session is already associated with current thread")
}
currentSession.set(session)
}
}
override fun close() {
isOpen = false
changesMultiplexer?.onClose(this)
logger.debug { "Close transient store." }
closed = true
val sessionsSize = sessions.size
if (sessionsSize > 0) {
logger.warn { "There're $sessionsSize open transient sessions. Print." }
if (logger.isDebugEnabled) {
sessions.asSequence()
.filterIsInstance()
.mapNotNull { session -> session.stack }
.forEach { sessionStackTrace ->
logger.warn(sessionStackTrace) { "Not closed session stack trace: " }
}
}
}
_persistentStore.close()
_persistentStore.environment.close()
}
override fun entityTypeExists(entityTypeName: String): Boolean {
return try {
_persistentStore.getEntityTypeId(entityTypeName) >= 0
} catch (e: Exception) {
false
}
}
override fun renameEntityTypeRefactoring(oldEntityTypeName: String, newEntityTypeName: String) {
val transientSession = transientSessionOrThrow
transientSession.addChangeAndRun {
(transientSession.persistentTransaction.store as PersistentEntityStore).renameEntityType(oldEntityTypeName, newEntityTypeName)
true
}
}
override fun deleteEntityTypeRefactoring(entityTypeName: String) {
val transientSession = transientSessionOrThrow
transientSession.addChangeAndRun {
transientSession.persistentTransaction.store.deleteEntityType(entityTypeName)
true
}
}
override fun deleteEntityRefactoring(entity: Entity) {
val transientSession = transientSessionOrThrow
if (entity is TransientEntity) {
transientSession.deleteEntity(entity)
} else {
val persistentEntity = entity.unwrapEntity()
transientSession.addChangeAndRun {
persistentEntity.delete()
true
}
}
}
override fun deleteLinksRefactoring(entity: Entity, linkName: String) {
val transientSession = transientSessionOrThrow
val persistentEntity = entity.unwrapEntity()
transientSession.addChangeAndRun {
persistentEntity.deleteLinks(linkName)
true
}
}
override fun deleteLinkRefactoring(entity: Entity, linkName: String, link: Entity) {
val transientSession = transientSessionOrThrow
val persistentEntity = entity.unwrapEntity()
val persistentLink = link.unwrapEntity()
transientSession.addChangeAndRun {
persistentEntity.deleteLink(linkName, persistentLink)
true
}
}
fun registerStoreSession(storeSession: TransientStoreSession): TransientStoreSession {
if (!sessions.add(storeSession)) {
throw IllegalArgumentException("Session is already registered.")
}
currentSession.set(storeSession)
return storeSession
}
fun unregisterStoreSession(storeSession: TransientStoreSession) {
if (!sessions.remove(storeSession)) {
throw IllegalArgumentException("Transient session wasn't previously registered.")
}
currentSession.remove()
}
override fun suspendThreadSession(): TransientStoreSession? {
assertOpen()
val current = threadSession
if (current != null) {
currentSession.remove()
}
return current
}
override fun addListener(listener: TransientStoreSessionListener) {
addListener(listener, 0)
}
override fun addListener(listener: TransientStoreSessionListener, priority: Int) {
withListeners {
if (any { it == listener }) {
throw IllegalStateException("$listener is already registered as a listener")
}
push(priority, listener)
}
}
override fun removeListener(listener: TransientStoreSessionListener) {
withListeners {
remove(listener)
}
}
internal fun forAllListeners(action: (TransientStoreSessionListener) -> Unit) {
withListeners {
toList()
}.forEach(action)
}
fun sessionsCount(): Int {
return sessions.size
}
fun dumpSessions(sb: StringBuilder) {
sessions.joinTo(sb, "\n")
}
override fun getCachedEnumValue(className: String, propName: String): Entity? {
return enumCache[getEnumKey(className, propName)]
}
fun setCachedEnumValue(className: String, propName: String, entity: Entity) {
enumCache[getEnumKey(className, propName)] = entity
}
/**
* Wait until underlying highAddress gets this value
*/
fun waitForPendingChanges(highAddress: Long) {
while ((_persistentStore.environment as EnvironmentImpl).log.tip.approvedHighAddress < highAddress) {
Thread.sleep(100)
}
}
private fun Entity.unwrapEntity(): PersistentEntity {
return when (this) {
is TransientEntity -> this.persistentEntity
is PersistentEntity -> this
else -> throw IllegalArgumentException("Cannot unwrap entity")
}
}
private fun withListeners(action: StablePriorityQueue.() -> T): T {
listeners.lock()
try {
return action(listeners)
} finally {
listeners.unlock()
}
}
private fun getEnumKey(className: String, propName: String) = "$propName@$className"
private fun assertOpen() {
// this flag isn't even volatile, but this is legacy behavior
if (closed) throw IllegalStateException("Transient store is closed.")
}
}
val StoreTransaction.highAddress
get() =
if (this is TransientStoreSession)
(persistentTransaction as PersistentStoreTransaction).environmentTransaction.highAddress
else {
(this as PersistentStoreTransaction).environmentTransaction.highAddress
}