diff --git a/build-docker.sh b/build-docker.sh index 32828cd711..076f1c9a58 100755 --- a/build-docker.sh +++ b/build-docker.sh @@ -10,10 +10,13 @@ WHITE='\033[0;37m' APP_NAME='docker images' APP_NAME=${BLUE}${APP_NAME}${NC} -echo -e "=== ${APP_NAME} build docker image...${WHITE}${NC}" && -docker build -f docker/Dockerfile -t openbaseorg/bco:local . -docker build -f docker/device-manager/openhab/Dockerfile -t openbaseorg/bco-device-manager-openhab:local --build-arg BCO_BASE_IMAGE_VERSION=local docker/device-manager/openhab -docker build -f docker/bco-demo/Dockerfile -t openbaseorg/bco-demo:local --build-arg BCO_BASE_IMAGE_VERSION=local docker/bco-demo +IMAGE_TAG=${1:-local} + +echo -e "=== ${APP_NAME} build docker image...${WHITE}${NC}" + +docker build -f docker/Dockerfile -t openbaseorg/bco:${IMAGE_TAG} . +docker build -f docker/device-manager/openhab/Dockerfile -t openbaseorg/bco-device-manager-openhab:${IMAGE_TAG} --build-arg BCO_BASE_IMAGE_VERSION=${IMAGE_TAG} docker/device-manager/openhab +docker build -f docker/bco-demo/Dockerfile -t openbaseorg/bco-demo:${IMAGE_TAG} --build-arg BCO_BASE_IMAGE_VERSION=${IMAGE_TAG} docker/bco-demo # use this for debugging purpose: DOCKER_BUILDKIT=0 docker build -f docker/Dockerfile --progress=plain . echo -e "=== ${APP_NAME} were ${GREEN}successfully${NC} build.${NC}" diff --git a/lib/jul b/lib/jul index 00675ad064..9a023a6171 160000 --- a/lib/jul +++ b/lib/jul @@ -1 +1 @@ -Subproject commit 00675ad06484adf749ae3b4231820c0f6bdd2cc5 +Subproject commit 9a023a617137d20596af504db9409cd92d3bf2ba diff --git a/lib/type b/lib/type index 99e866c784..05eba9bc57 160000 --- a/lib/type +++ b/lib/type @@ -1 +1 @@ -Subproject commit 99e866c78450e7ae410f2bc594a239b5b53ab970 +Subproject commit 05eba9bc579a8b7fcd006eda89f462d11a6a0fd1 diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/BcoGraphQlApiSpringBootApplication.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/BcoGraphQlApiSpringBootApplication.kt index 6cb6233d6e..2e279d3874 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/BcoGraphQlApiSpringBootApplication.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/BcoGraphQlApiSpringBootApplication.kt @@ -57,7 +57,8 @@ import org.springframework.context.annotation.Bean * License along with this program. If not, see * . * #L% - */@SpringBootApplication + */ +@SpringBootApplication open class BcoGraphQlApiSpringBootApplication { private var injector: Injector? = null @@ -84,6 +85,8 @@ open class BcoGraphQlApiSpringBootApplication { ) val unitDataOutputType = schema.getType("openbase_type_domotic_unit_UnitData") as GraphQLOutputType? val unitConfigOutputType = schema.getType("openbase_type_domotic_unit_UnitConfig") as GraphQLOutputType? + val userMessageOutputType = + schema.getType("openbase_type_domotic_communication_UserMessage") as GraphQLOutputType? val unitFilterInputType = schema.getType("Input_openbase_type_domotic_unit_UnitFilter") as GraphQLInputType? val unitFilterInputConverter = GqlInputConverter.newBuilder().add(UnitFilterType.UnitFilter.getDescriptor().file).build() @@ -99,6 +102,11 @@ open class BcoGraphQlApiSpringBootApplication { .argument(GraphQLArgument.newArgument().name("includeDisabledUnits").type(Scalars.GraphQLBoolean)) .build() ) + builder.field( + GraphQLFieldDefinition.newFieldDefinition().name("userMessages") + .type(GraphQLList.list(userMessageOutputType)) + .build() + ) val codeRegistry = GraphQLCodeRegistry.newCodeRegistry(schema.codeRegistry) .dataFetcher(FieldCoordinates.coordinates("Subscription", "units"), DataFetcher { dataFetchingEnvironment -> val unitFilter = unitFilterInputConverter.createProtoBuf( @@ -122,6 +130,11 @@ open class BcoGraphQlApiSpringBootApplication { } SubscriptionModule.subscribeUnitConfigs(unitFilter, includeDisabledUnits) }) + .dataFetcher( + FieldCoordinates.coordinates("Subscription", "userMessages"), + DataFetcher { + SubscriptionModule.subscribeUserMessages() + }) .build() return GraphQLSchema.newSchema(schema) .subscription(builder.build()) diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/AbstractBCOGraphQLContext.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/AbstractBCOGraphQLContext.kt index 5dc614b01f..f43f611efd 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/AbstractBCOGraphQLContext.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/AbstractBCOGraphQLContext.kt @@ -34,7 +34,7 @@ abstract class AbstractBCOGraphQLContext( abstract val languageCode: String? val auth: AuthTokenType.AuthToken? - get() = AuthTokenType.AuthToken.newBuilder().setAuthenticationToken(token).build() + get() = token?.let { AuthTokenType.AuthToken.newBuilder().setAuthenticationToken(it).build() } companion object { const val DATA_LOADER_UNITS = "units" diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/schema/RegistrySchemaModule.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/schema/RegistrySchemaModule.kt index aa0f40b500..53c5c4f24b 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/schema/RegistrySchemaModule.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/schema/RegistrySchemaModule.kt @@ -10,6 +10,9 @@ import org.openbase.bco.api.graphql.error.GenericError import org.openbase.bco.api.graphql.error.ServerError import org.openbase.bco.authentication.lib.SessionManager import org.openbase.bco.authentication.lib.iface.BCOSession +import org.openbase.bco.registry.message.remote.registerUserMessageAuthenticated +import org.openbase.bco.registry.message.remote.removeUserMessageAuthenticated +import org.openbase.bco.registry.message.remote.updateUserMessageAuthenticated import org.openbase.bco.registry.remote.Registries import org.openbase.bco.registry.remote.session.BCOSessionImpl import org.openbase.bco.registry.unit.remote.registerUnitConfigAuthenticated @@ -21,6 +24,7 @@ import org.openbase.jul.extension.type.processing.LabelProcessor.getBestMatch import org.openbase.jul.extension.type.processing.LabelProcessor.replace import org.openbase.type.configuration.EntryType import org.openbase.type.configuration.MetaConfigType +import org.openbase.type.domotic.communication.UserMessageType.UserMessage import org.openbase.type.domotic.service.ServiceTemplateType import org.openbase.type.domotic.unit.UnitConfigType import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig @@ -56,7 +60,8 @@ import java.util.concurrent.* * License along with this program. If not, see * . * #L% - */ class RegistrySchemaModule : SchemaModule() { + */ +class RegistrySchemaModule : SchemaModule() { /** * Check if an authentication token retrieved by the login method is still valid. * @@ -157,6 +162,10 @@ import java.util.concurrent.* @Arg("includeDisabledUnits") includeDisabledUnits: Boolean?, ): ImmutableList = getUnitConfigs(unitFilter, includeDisabledUnits) + @Query("userMessages") + @Throws(BCOGraphQLError::class) + fun userMessages(): ImmutableList = getUserMessages() + @Query("gatewayClasses") @Throws(CouldNotPerformException::class, InterruptedException::class) fun gatewayClasses(): ImmutableList = @@ -401,7 +410,7 @@ import java.util.concurrent.* break } } - if (!entry.value.isEmpty()) { + if (entry.value.isNotEmpty()) { metaConfigBuilder.addEntry(entry) } @@ -419,6 +428,92 @@ import java.util.concurrent.* throw GenericError(ex) } + @Mutation("updateUserMessage") + @Throws(BCOGraphQLError::class) + fun updateUserMessage( + @Arg("userMessage") userMessage: UserMessage, + env: DataFetchingEnvironment, + ): UserMessage = try { + val userMessageBuilder = Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ) + .getUserMessageById(userMessage.id) + .toBuilder() + userMessageBuilder.mergeFromWithoutRepeatedFields(userMessage) + Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ).updateUserMessageAuthenticated( + userMessageBuilder.build(), + env.context.auth + )[ServerError.BCO_TIMEOUT_SHORT, ServerError.BCO_TIMEOUT_TIME_UNIT] + } catch (ex: RuntimeException) { + throw GenericError(ex) + } catch (ex: CouldNotPerformException) { + throw GenericError(ex) + } catch (ex: InterruptedException) { + throw GenericError(ex) + } catch (ex: ExecutionException) { + throw GenericError(ex) + } catch (ex: TimeoutException) { + throw GenericError(ex) + } + + @Mutation("removeUserMessage") + @Throws(BCOGraphQLError::class) + fun removeUserMessage( + @Arg("unitId") unitId: String?, + env: DataFetchingEnvironment, + ): UserMessage = try { + val userMessage = Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ).getUserMessageById(unitId) + Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ).removeUserMessageAuthenticated( + userMessage, + env.context.auth + )[ServerError.BCO_TIMEOUT_SHORT, ServerError.BCO_TIMEOUT_TIME_UNIT] + } catch (ex: RuntimeException) { + throw GenericError(ex) + } catch (ex: CouldNotPerformException) { + throw GenericError(ex) + } catch (ex: InterruptedException) { + throw GenericError(ex) + } catch (ex: ExecutionException) { + throw GenericError(ex) + } catch (ex: TimeoutException) { + throw GenericError(ex) + } + + @Mutation("registerUserMessage") + @Throws(BCOGraphQLError::class) + fun registerUserMessage( + @Arg("userMessage") userMessage: UserMessage?, + env: DataFetchingEnvironment, + ): UserMessage = try { + Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ).registerUserMessageAuthenticated( + userMessage, + env.context.auth + )[ServerError.BCO_TIMEOUT_SHORT, ServerError.BCO_TIMEOUT_TIME_UNIT] + } catch (ex: RuntimeException) { + throw GenericError(ex) + } catch (ex: CouldNotPerformException) { + throw GenericError(ex) + } catch (ex: InterruptedException) { + throw GenericError(ex) + } catch (ex: ExecutionException) { + throw GenericError(ex) + } catch (ex: TimeoutException) { + throw GenericError(ex) + } + companion object { @Throws(BCOGraphQLError::class) fun getUnitConfigs( @@ -438,5 +533,22 @@ import java.util.concurrent.* } catch (ex: InterruptedException) { throw GenericError(ex) } + + @Throws(BCOGraphQLError::class) + fun getUserMessages( + ): ImmutableList = try { + ImmutableList.copyOf( + Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ).userMessages + ) + } catch (ex: RuntimeException) { + throw GenericError(ex) + } catch (ex: CouldNotPerformException) { + throw GenericError(ex) + } catch (ex: InterruptedException) { + throw GenericError(ex) + } } } diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/MessageRegistrySubscriptionObserver.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/MessageRegistrySubscriptionObserver.kt new file mode 100644 index 0000000000..c4793c4358 --- /dev/null +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/MessageRegistrySubscriptionObserver.kt @@ -0,0 +1,50 @@ +package org.openbase.bco.api.graphql.subscriptions + +import com.google.common.collect.ImmutableList +import org.openbase.bco.api.graphql.error.ServerError +import org.openbase.bco.api.graphql.schema.RegistrySchemaModule +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.pattern.provider.DataProvider +import org.openbase.type.domotic.communication.UserMessageType +import org.openbase.type.domotic.registry.MessageRegistryDataType + +class MessageRegistrySubscriptionObserver() : + AbstractObserverMapper, MessageRegistryDataType.MessageRegistryData, List>() { + private val userMessages: MutableList + + init { + Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ) + userMessages = ArrayList( + RegistrySchemaModule.getUserMessages() + ) + } + + @Throws(Exception::class) + override fun update( + source: DataProvider, + target: MessageRegistryDataType.MessageRegistryData, + ) { + val newUserMessages: ImmutableList = + RegistrySchemaModule.getUserMessages() + if (newUserMessages == userMessages) { + // nothing has changed + return + } + + // store update + userMessages.clear() + userMessages.addAll(newUserMessages) + super.update(source, target) + } + + @Throws(Exception::class) + override fun mapData( + source: DataProvider, + data: MessageRegistryDataType.MessageRegistryData, + ): List { + return userMessages + } +} diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/SubscriptionModule.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/SubscriptionModule.kt index a877304693..3bb7b0e822 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/SubscriptionModule.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/SubscriptionModule.kt @@ -1,26 +1,26 @@ package org.openbase.bco.api.graphql.subscriptions -import com.google.common.collect.ImmutableList import com.google.protobuf.Message import io.reactivex.BackpressureStrategy import org.openbase.bco.api.graphql.error.BCOGraphQLError import org.openbase.bco.api.graphql.error.GenericError import org.openbase.bco.api.graphql.error.ServerError -import org.openbase.bco.api.graphql.schema.RegistrySchemaModule import org.openbase.bco.dal.lib.layer.unit.Unit +import org.openbase.bco.dal.lib.layer.unit.UnitRemote import org.openbase.bco.dal.remote.layer.unit.CustomUnitPool import org.openbase.bco.registry.remote.Registries import org.openbase.jul.exception.CouldNotPerformException import org.openbase.jul.extension.protobuf.ProtoBufBuilderProcessor.merge import org.openbase.jul.pattern.Observer import org.openbase.jul.pattern.provider.DataProvider +import org.openbase.type.domotic.communication.UserMessageType.UserMessage +import org.openbase.type.domotic.registry.MessageRegistryDataType.MessageRegistryData import org.openbase.type.domotic.registry.UnitRegistryDataType.UnitRegistryData import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig import org.openbase.type.domotic.unit.UnitDataType import org.openbase.type.domotic.unit.UnitFilterType.UnitFilter import org.reactivestreams.Publisher import org.slf4j.LoggerFactory -import java.util.function.Consumer /*- * #%L @@ -42,7 +42,8 @@ import java.util.function.Consumer * License along with this program. If not, see * . * #L% - */ object SubscriptionModule { + */ +object SubscriptionModule { private val log = LoggerFactory.getLogger(SubscriptionModule::class.java) //TODO: what is a good strategy here @@ -51,15 +52,15 @@ import java.util.function.Consumer @Throws(BCOGraphQLError::class) fun subscribeUnits(unitFilter: UnitFilter): Publisher { return try { - val subscriptionUnitPool = CustomUnitPool() + val subscriptionUnitPool = CustomUnitPool>() subscriptionUnitPool.init(unitFilter) AbstractObserverMapper.createObservable( - Consumer { observer: Observer, Message> -> + { observer: Observer, Message> -> subscriptionUnitPool.addDataObserver( observer ) }, - Consumer { observer: Observer, Message> -> + { observer: Observer, Message> -> subscriptionUnitPool.removeDataObserver(observer) }, object : AbstractObserverMapper, Message, UnitDataType.UnitData>() { @@ -94,18 +95,18 @@ import java.util.function.Consumer includeDisabledUnits: Boolean, ): Publisher> { return try { - val observer = RegistrySubscriptionObserver(unitFilter, includeDisabledUnits) + val observer = UnitRegistrySubscriptionObserver(unitFilter, includeDisabledUnits) val unitRegistry = Registries.getUnitRegistry( ServerError.BCO_TIMEOUT_SHORT, ServerError.BCO_TIMEOUT_TIME_UNIT ) AbstractObserverMapper.createObservable( - Consumer { observer: Observer, UnitRegistryData> -> + { observer: Observer, UnitRegistryData> -> unitRegistry.addDataObserver( observer ) }, - Consumer { observer: Observer, UnitRegistryData> -> + { observer: Observer, UnitRegistryData> -> unitRegistry.removeDataObserver(observer) }, observer @@ -119,50 +120,31 @@ import java.util.function.Consumer } } - class RegistrySubscriptionObserver( - private val unitFilter: UnitFilter, - private val includeDisabledUnits: Boolean, - ) : AbstractObserverMapper, UnitRegistryData, List>() { - private val unitConfigs: MutableList - - init { - Registries.getUnitRegistry( + @Throws(BCOGraphQLError::class) + fun subscribeUserMessages(): Publisher> { + return try { + val observer = MessageRegistrySubscriptionObserver() + val messageRegistry = Registries.getMessageRegistry( ServerError.BCO_TIMEOUT_SHORT, ServerError.BCO_TIMEOUT_TIME_UNIT ) - unitConfigs = ArrayList( - RegistrySchemaModule.getUnitConfigs( - unitFilter, includeDisabledUnits - ) - ) - } - - @Throws(Exception::class) - override fun update( - source: DataProvider, - target: UnitRegistryData, - ) { - val newUnitConfigs: ImmutableList = - RegistrySchemaModule.getUnitConfigs( - unitFilter, includeDisabledUnits - ) - if (newUnitConfigs == unitConfigs) { - // nothing has changed - return - } - - // store update - unitConfigs.clear() - unitConfigs.addAll(newUnitConfigs) - super.update(source, target) - } - - @Throws(Exception::class) - override fun mapData( - source: DataProvider, - data: UnitRegistryData, - ): List { - return unitConfigs + AbstractObserverMapper.createObservable( + { observer: Observer, MessageRegistryData> -> + messageRegistry.addDataObserver( + observer + ) + }, + { observer: Observer, MessageRegistryData> -> + messageRegistry.removeDataObserver(observer) + }, + observer + ).toFlowable(BACKPRESSURE_STRATEGY) + } catch (ex: RuntimeException) { + throw GenericError(ex) + } catch (ex: CouldNotPerformException) { + throw GenericError(ex) + } catch (ex: InterruptedException) { + throw GenericError(ex) } } } diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/UnitRegistrySubscriptionObserver.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/UnitRegistrySubscriptionObserver.kt new file mode 100644 index 0000000000..4eea1a30ec --- /dev/null +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/UnitRegistrySubscriptionObserver.kt @@ -0,0 +1,57 @@ +package org.openbase.bco.api.graphql.subscriptions + +import com.google.common.collect.ImmutableList +import org.openbase.bco.api.graphql.error.ServerError +import org.openbase.bco.api.graphql.schema.RegistrySchemaModule +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.pattern.provider.DataProvider +import org.openbase.type.domotic.registry.UnitRegistryDataType +import org.openbase.type.domotic.unit.UnitConfigType +import org.openbase.type.domotic.unit.UnitFilterType + +class UnitRegistrySubscriptionObserver( + private val unitFilter: UnitFilterType.UnitFilter, + private val includeDisabledUnits: Boolean, +) : AbstractObserverMapper, UnitRegistryDataType.UnitRegistryData, List>() { + private val unitConfigs: MutableList + + init { + Registries.getUnitRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ) + unitConfigs = ArrayList( + RegistrySchemaModule.getUnitConfigs( + unitFilter, includeDisabledUnits + ) + ) + } + + @Throws(Exception::class) + override fun update( + source: DataProvider, + target: UnitRegistryDataType.UnitRegistryData, + ) { + val newUnitConfigs: ImmutableList = + RegistrySchemaModule.getUnitConfigs( + unitFilter, includeDisabledUnits + ) + if (newUnitConfigs == unitConfigs) { + // nothing has changed + return + } + + // store update + unitConfigs.clear() + unitConfigs.addAll(newUnitConfigs) + super.update(source, target) + } + + @Throws(Exception::class) + override fun mapData( + source: DataProvider, + data: UnitRegistryDataType.UnitRegistryData, + ): List { + return unitConfigs + } +} diff --git a/module/app/preset/src/main/java/org/openbase/bco/app/preset/DeviceNotificationApp.kt b/module/app/preset/src/main/java/org/openbase/bco/app/preset/DeviceNotificationApp.kt new file mode 100644 index 0000000000..7c1a8e4b0e --- /dev/null +++ b/module/app/preset/src/main/java/org/openbase/bco/app/preset/DeviceNotificationApp.kt @@ -0,0 +1,148 @@ +package org.openbase.bco.app.preset + +import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController +import org.openbase.bco.dal.lib.layer.service.ServiceStateProcessor +import org.openbase.bco.dal.remote.layer.unit.BatteryRemote +import org.openbase.bco.dal.remote.layer.unit.CustomUnitPool +import org.openbase.bco.registry.message.remote.registerUserMessageAuthenticated +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.extension.type.processing.LabelProcessor +import org.openbase.jul.extension.type.processing.MultiLanguageTextProcessor +import org.openbase.jul.extension.type.processing.TimestampProcessor +import org.openbase.jul.schedule.GlobalScheduledExecutorService +import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription +import org.openbase.type.domotic.communication.UserMessageType.UserMessage +import org.openbase.type.domotic.service.ServiceStateDescriptionType.ServiceStateDescription +import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServiceType +import org.openbase.type.domotic.state.ActivationStateType.ActivationState +import org.openbase.type.domotic.state.BatteryStateType.BatteryState +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig +import org.openbase.type.domotic.unit.UnitFilterType.UnitFilter +import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType +import org.openbase.type.domotic.unit.dal.BatteryDataType.BatteryData +import org.openbase.type.language.MultiLanguageTextType.MultiLanguageText +import org.slf4j.LoggerFactory +import java.time.Duration +import java.util.* +import java.util.concurrent.ScheduledFuture +import java.util.concurrent.TimeUnit + +/** + * An app that notifies users about devices with empty batteries or offline states. + */ +class DeviceNotificationApp : AbstractAppController() { + + private val batteryPool = CustomUnitPool() + private var task: ScheduledFuture<*>? = null + + init { + batteryPool.init( + UnitFilter.newBuilder().setProperties(UnitConfig.newBuilder().setUnitType(UnitType.BATTERY)).build() + ) + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun execute(activationState: ActivationState): ActionDescription = + activationState.responsibleAction.also { + batteryPool.activate() + task?.cancel(false) + task = GlobalScheduledExecutorService.scheduleWithFixedDelay( + ::checkDevices, + INITIAL_VALIDATION_DELAY.toMillis(), + VALIDATION_PERIOD.toMillis(), + TimeUnit.MILLISECONDS + ) + LOGGER.trace(getLabel() + " is running.") + } + + @Throws(InterruptedException::class, CouldNotPerformException::class) + override fun stop(activationState: ActivationState) = + super.stop(activationState).also { + batteryPool.deactivate() + task?.cancel(true) + LOGGER.trace(getLabel() + " has been stopped.") + } + + fun checkDevices() { + try { + batteryPool.internalUnitList + .onEach { remote -> remote.waitForData() } + .filter { remote -> + when (remote.data.batteryState.value) { + BatteryState.State.LOW, BatteryState.State.CRITICAL, BatteryState.State.UNKNOWN -> true + else -> false + } + } + .map { remote -> + val messageBuilder: UserMessage.Builder = UserMessage.newBuilder() + val textBuilder: MultiLanguageText.Builder = MultiLanguageText.newBuilder() + + MultiLanguageTextProcessor.addMultiLanguageText( + textBuilder, + Locale.ENGLISH, + "Battery level of ${ + LabelProcessor.getBestMatch( + Locale.ENGLISH, + remote.config.label + ) + } is ${remote.data.batteryState.value}" + ) + + MultiLanguageTextProcessor.addMultiLanguageText( + textBuilder, + Locale.GERMAN, + "Batterieladung von ${ + LabelProcessor.getBestMatch( + Locale.GERMAN, + remote.config.label + ) + } ist ${remote.data.batteryState.value}" + ) + + messageBuilder.id = UUID.randomUUID().toString() + messageBuilder.messageType = UserMessage.MessageType.WARNING + messageBuilder.timestamp = TimestampProcessor.getCurrentTimestamp() + messageBuilder.text = textBuilder.build() + messageBuilder.senderId = userConfig.id + messageBuilder.addCondition( + with(ServiceStateDescription.newBuilder()) { + setUnitId(remote.id) + setServiceType(ServiceType.BATTERY_STATE_SERVICE) + setServiceStateClassName(BatteryState::class.java.name) + setServiceState( + ServiceStateProcessor.serializeServiceState( + BatteryState.newBuilder().setValue(remote.data.batteryState.value).build(), + false, + ) + ) + }.also { ServiceStateProcessor.deserializeServiceState(it) } + ) + + // validate if message already exist + messageBuilder.build() to Registries.getMessageRegistry() + .getUserMessagesByText( + MultiLanguageTextProcessor.getBestMatch( + Locale.ENGLISH, messageBuilder.text + ), Locale.ENGLISH + ).isNotEmpty() + } + .filterNot { (_, exist) -> exist } + .forEach { (message, _) -> + Registries.getMessageRegistry() + .registerUserMessageAuthenticated(message, token) + .get(5, TimeUnit.SECONDS) + } + } catch (e: CouldNotPerformException) { + ExceptionPrinter.printHistory("Could not check device states!", e, logger) + } + } + + companion object { + private val LOGGER = LoggerFactory.getLogger(TemplateApp::class.java) + + private val VALIDATION_PERIOD: Duration = Duration.ofHours(24) + private val INITIAL_VALIDATION_DELAY: Duration = Duration.ofHours(1) + } +} diff --git a/module/app/preset/src/main/java/org/openbase/bco/app/preset/TemplateApp.kt b/module/app/preset/src/main/java/org/openbase/bco/app/preset/TemplateApp.kt new file mode 100644 index 0000000000..13f5e0dbdb --- /dev/null +++ b/module/app/preset/src/main/java/org/openbase/bco/app/preset/TemplateApp.kt @@ -0,0 +1,67 @@ +package org.openbase.bco.app.preset + +import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription +import org.openbase.type.domotic.state.ActivationStateType +import org.openbase.type.domotic.state.ActivationStateType.ActivationState +import org.openbase.type.domotic.unit.UnitConfigType +import org.slf4j.LoggerFactory + +/* +* #%L +* BCO App Preset +* %% +* Copyright (C) 2018 - 2021 openbase.org +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public +* License along with this program. If not, see +* . +* #L% +*/ /** + * A class that can be used as template for new apps. + */ +class TemplateApp : AbstractAppController() { + + private var executing = false + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun applyConfigUpdate(config: UnitConfigType.UnitConfig): UnitConfigType.UnitConfig = + getManageWriteLockInterruptible(this).use { + super.applyConfigUpdate(config).also { + LOGGER.info(getLabel() + " config has been changed.") + } + } + + override fun shutdown() = super.shutdown().also { + LOGGER.info(getLabel() + " is shutting down.") + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun execute(activationState: ActivationStateType.ActivationState): ActionDescription = + activationState.responsibleAction.also { + executing = true + LOGGER.info(getLabel() + " is running.") + } + + @Throws(InterruptedException::class, CouldNotPerformException::class) + override fun stop(activationState: ActivationState) = + super.stop(activationState).also { + executing = false + LOGGER.info(getLabel() + " has been stopped.") + } + + companion object { + private val LOGGER = LoggerFactory.getLogger(TemplateApp::class.java) + } +} diff --git a/module/app/preset/src/test/java/org/openbase/bco/app/preset/DeviceNotificationAppTest.kt b/module/app/preset/src/test/java/org/openbase/bco/app/preset/DeviceNotificationAppTest.kt new file mode 100644 index 0000000000..89c0e99377 --- /dev/null +++ b/module/app/preset/src/test/java/org/openbase/bco/app/preset/DeviceNotificationAppTest.kt @@ -0,0 +1,155 @@ +package org.openbase.bco.app.preset + +import io.kotest.matchers.equals.shouldBeEqual +import org.junit.jupiter.api.Test +import org.openbase.app.test.agent.AbstractBCOAppManagerTest +import org.openbase.bco.authentication.lib.SessionManager +import org.openbase.bco.authentication.lib.future.AuthenticatedValueFuture +import org.openbase.bco.dal.control.layer.unit.BatteryController +import org.openbase.bco.dal.lib.layer.unit.Unit +import org.openbase.bco.dal.lib.state.States +import org.openbase.bco.dal.remote.layer.unit.BatteryRemote +import org.openbase.bco.dal.remote.layer.unit.Units +import org.openbase.bco.dal.remote.layer.unit.util.UnitStateAwaiter +import org.openbase.bco.registry.lib.util.UnitConfigProcessor +import org.openbase.bco.registry.mock.MockRegistry +import org.openbase.bco.registry.remote.Registries +import org.openbase.bco.registry.unit.core.plugin.UnitUserCreationPlugin +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.extension.type.processing.MultiLanguageTextProcessor +import org.openbase.type.domotic.authentication.AuthTokenType +import org.openbase.type.domotic.authentication.AuthenticationTokenType +import org.openbase.type.domotic.communication.UserMessageType.UserMessage +import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServiceType +import org.openbase.type.domotic.state.ActivationStateType.ActivationState +import org.openbase.type.domotic.state.BatteryStateType.BatteryState +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig +import org.openbase.type.domotic.unit.UnitTemplateType +import org.openbase.type.domotic.unit.app.AppDataType.AppData +import java.util.* +import java.util.concurrent.ExecutionException +import java.util.concurrent.TimeUnit + +class DeviceNotificationAppTest : AbstractBCOAppManagerTest() { + + private val APP_ALIAS = "Device_Notification_App_Test" + override fun getAppClass() = DeviceNotificationApp::class.java + + override fun getAppConfig(): UnitConfig.Builder = MockRegistry.generateAppConfig( + APP_ALIAS, + MockRegistry.ALIAS_LOCATION_ROOT_PARADISE + ) + + private fun getUserMessagesOfUnit(unit: Unit<*>): List = run { + Registries.getMessageRegistry().requestData().get(5, TimeUnit.SECONDS) + }.let { + Registries.getMessageRegistry().userMessages + }.filter { it.conditionList.any { condition -> condition.unitId == unit.id } } + + + @Throws(CouldNotPerformException::class, InterruptedException::class) + private fun requestAuthToken(unitConfig: UnitConfig): AuthTokenType.AuthToken { + try { + val userConfig: UnitConfig = UnitUserCreationPlugin.findUser( + unitConfig.id, Registries.getUnitRegistry(true).getUnitConfigsByUnitType( + UnitTemplateType.UnitTemplate.UnitType.USER + ) + ) + val authenticationToken = + AuthenticationTokenType.AuthenticationToken.newBuilder().setUserId(userConfig.getId()).build() + val sessionManager = SessionManager() + sessionManager.loginUser(userConfig.getId(), true) + val authenticatedValue = sessionManager.initializeRequest(authenticationToken, null) + return AuthTokenType.AuthToken.newBuilder().setAuthenticationToken( + AuthenticatedValueFuture( + Registries.getUnitRegistry().requestAuthenticationTokenAuthenticated(authenticatedValue), + String::class.java, + authenticatedValue.ticketAuthenticatorWrapper, + sessionManager + ).get() + ).build() + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException( + "Could not create authentication token for " + this + " " + UnitConfigProcessor.getDefaultAlias( + unitConfig, + unitConfig.id + ), ex + ) + } catch (ex: ExecutionException) { + throw CouldNotPerformException( + "Could not create authentication token for " + this + " " + UnitConfigProcessor.getDefaultAlias( + unitConfig, + unitConfig.id + ), ex + ) + } + } + + @Test + fun testLowBatteryNotification() { + println("testAbsenceEnergySavingAgent") + + val unitStateAwaiter = UnitStateAwaiter(appRemote) + unitStateAwaiter.waitForState { data: AppData -> + data.activationState.getValue() == ActivationState.State.ACTIVE + } + + val location = Units.getUnitByAlias(MockRegistry.ALIAS_LOCATION_STAIRWAY_TO_HEAVEN, true, Units.LOCATION) + val batteryRemote: BatteryRemote = + location.getUnits(UnitTemplateType.UnitTemplate.UnitType.BATTERY, true, Units.BATTERY).first() + + val batteryStateAwaiter = UnitStateAwaiter(batteryRemote) + val batteryController = + deviceManagerLauncher.launchable!!.unitControllerRegistry.get(batteryRemote.getId()) as BatteryController + + // verify ground truth + getUserMessagesOfUnit(batteryRemote) shouldBeEqual emptyList() + + appController!!.checkDevices() + + // verify unknown battery state notification + getUserMessagesOfUnit(batteryRemote).size shouldBeEqual 1 + + batteryController.applyServiceState(States.Battery.OK, ServiceType.BATTERY_STATE_SERVICE) + batteryStateAwaiter.waitForState { it.batteryState.value == BatteryState.State.OK } + batteryRemote.requestData().get(5, TimeUnit.SECONDS) + + + appController!!.checkDevices() + messageManagerLauncher.launchable!!.removeOutdatedMessages(requestAuthToken(appConfig!!)) + + // verify everything is ok again + getUserMessagesOfUnit(batteryRemote) shouldBeEqual emptyList() + + batteryController.applyServiceState(States.Battery.CRITICAL, ServiceType.BATTERY_STATE_SERVICE) + batteryStateAwaiter.waitForState { it.batteryState.value == BatteryState.State.CRITICAL } + + appController!!.checkDevices() + messageManagerLauncher.launchable!!.removeOutdatedMessages((requestAuthToken(appConfig!!))) + + // verify critical battery state notification + getUserMessagesOfUnit(batteryRemote) + .also { it.size shouldBeEqual 1 } + .first().apply { + messageType shouldBeEqual UserMessage.MessageType.WARNING + MultiLanguageTextProcessor.getBestMatch( + Locale.GERMAN, + text + ) shouldBeEqual "Batterieladung von F Motion Sensor Device Stairway ist CRITICAL" + MultiLanguageTextProcessor.getBestMatch( + Locale.ENGLISH, + text + ) shouldBeEqual "Battery level of F Motion Sensor Device Stairway is CRITICAL" + + } + + batteryController.applyServiceState(States.Battery.OK, ServiceType.BATTERY_STATE_SERVICE) + batteryStateAwaiter.waitForState { it.batteryState.value == BatteryState.State.OK } + + appController!!.checkDevices() + messageManagerLauncher.launchable!!.removeOutdatedMessages((requestAuthToken(appConfig!!))) + + // verify all messages are removed after the battery has been replaced. + getUserMessagesOfUnit(batteryRemote) shouldBeEqual emptyList() + } +} diff --git a/module/app/preset/src/test/java/org/openbase/bco/app/preset/agent/PresenceLightAgentTest.java b/module/app/preset/src/test/java/org/openbase/bco/app/preset/agent/PresenceLightAgentTest.java index c3c3be0b05..cb88183305 100644 --- a/module/app/preset/src/test/java/org/openbase/bco/app/preset/agent/PresenceLightAgentTest.java +++ b/module/app/preset/src/test/java/org/openbase/bco/app/preset/agent/PresenceLightAgentTest.java @@ -10,24 +10,21 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . * #L% */ -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.openbase.app.test.agent.AbstractBCOAgentManagerTest; -import org.junit.jupiter.api.Test; import org.openbase.bco.dal.control.layer.unit.LightSensorController; import org.openbase.bco.dal.control.layer.unit.MotionDetectorController; import org.openbase.bco.dal.lib.state.States; @@ -39,29 +36,26 @@ import org.openbase.bco.dal.remote.layer.unit.Units; import org.openbase.bco.dal.remote.layer.unit.location.LocationRemote; import org.openbase.bco.dal.remote.layer.unit.util.UnitStateAwaiter; -import org.openbase.bco.dal.visual.action.BCOActionInspector; import org.openbase.bco.registry.mock.MockRegistry; -import org.openbase.jps.core.JPService; -import org.openbase.jps.preset.JPDebugMode; -import org.openbase.jps.preset.JPVerbose; import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.NotAvailableException; import org.openbase.jul.extension.type.processing.MultiLanguageTextProcessor; import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; import org.openbase.type.domotic.action.ActionPriorityType.ActionPriority.Priority; -import org.openbase.type.domotic.state.IlluminanceStateType.IlluminanceState; -import org.openbase.type.domotic.state.MotionStateType.MotionState.State; -import org.openbase.type.domotic.unit.dal.LightSensorDataType.LightSensorData; -import org.slf4j.LoggerFactory; import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServiceType; +import org.openbase.type.domotic.state.IlluminanceStateType.IlluminanceState; import org.openbase.type.domotic.state.MotionStateType.MotionState; +import org.openbase.type.domotic.state.MotionStateType.MotionState.State; import org.openbase.type.domotic.state.PowerStateType.PowerState; import org.openbase.type.domotic.state.PresenceStateType.PresenceState; import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType; import org.openbase.type.domotic.unit.dal.ColorableLightDataType.ColorableLightData; +import org.openbase.type.domotic.unit.dal.LightSensorDataType.LightSensorData; import org.openbase.type.domotic.unit.dal.MotionDetectorDataType.MotionDetectorData; import org.openbase.type.domotic.unit.location.LocationDataType.LocationData; +import org.slf4j.LoggerFactory; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * * @author Timo Michalski @@ -88,12 +82,12 @@ public class PresenceLightAgentTest extends AbstractBCOAgentManagerTest { //@BeforeAll //uncomment to enable debug mode public static void showActionInspector() throws Throwable { - JPService.registerProperty(JPDebugMode.class, true); - JPService.registerProperty(JPVerbose.class, true); + // JPService.registerProperty(JPDebugMode.class, true); + // JPService.registerProperty(JPVerbose.class, true); // uncomment to visualize action inspector during tests - String[] args = {}; - new Thread(() -> BCOActionInspector.main(args)).start(); + // String[] args = {}; + // new Thread(() -> BCOActionInspector.main(args)).start(); } @Override @@ -220,7 +214,7 @@ public void testPresenceLightAgent() throws Exception { for (ActionDescription actionDescription : colorableLightRemote.requestData().get().getActionList()) { // ignore termination action because its always on the stack - if(actionDescription.getPriority() == Priority.TERMINATION) { + if (actionDescription.getPriority() == Priority.TERMINATION) { continue; } diff --git a/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.java b/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.java deleted file mode 100644 index 76ac4a44bc..0000000000 --- a/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.openbase.app.test.agent; - -/*- - * #%L - * BCO App Test Framework - * %% - * Copyright (C) 2018 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Timeout; -import org.openbase.bco.dal.remote.layer.unit.Units; -import org.openbase.bco.dal.remote.layer.unit.app.AppRemote; -import org.openbase.bco.dal.remote.layer.unit.util.UnitStateAwaiter; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.printer.ExceptionPrinter; -import org.openbase.jul.extension.type.processing.LabelProcessor; -import org.openbase.type.domotic.state.ActivationStateType; -import org.openbase.type.domotic.state.ConnectionStateType.ConnectionState; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.UnitTemplateType; -import org.openbase.type.domotic.unit.app.AppClassType.AppClass; -import org.slf4j.LoggerFactory; - -import java.util.Locale; -import java.util.concurrent.TimeUnit; - -public abstract class AbstractBCOAppManagerTest extends BCOAppTest { - - private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(AbstractBCOAgentManagerTest.class); - - protected AppClass appClass = null; - protected UnitConfig appConfig = null; - protected AppRemote appRemote = null; - - @BeforeEach - @Timeout(30) - public void prepareAppManager() throws Exception { - try { - // setup and register app class - AppClass.Builder appClassBuilder = AppClass.newBuilder(); - LabelProcessor.addLabel(appClassBuilder.getLabelBuilder(), Locale.ENGLISH, getAppClass().getSimpleName().replace("App", "")); - this.appClass = Registries.getClassRegistry().registerAppClass(appClassBuilder.build()).get(5, TimeUnit.SECONDS); - - UnitConfig.Builder appConfigBuilder = getAppConfig(); - appConfigBuilder.getAppConfigBuilder().setAppClassId(this.appClass.getId()); - appConfigBuilder.setUnitType(UnitTemplateType.UnitTemplate.UnitType.APP); - // register app - this.appConfig = Registries.getUnitRegistry().registerUnitConfig(appConfigBuilder.build()).get(5, TimeUnit.SECONDS); - // retrieve remote and activate app - this.appRemote = Units.getUnit(this.appConfig, true, Units.APP); - if (!this.appConfig.getAppConfig().getAutostart()) { - // activate app if not in auto start - waitForExecution(this.appRemote.setActivationState(ActivationStateType.ActivationState.newBuilder().setValue(ActivationStateType.ActivationState.State.ACTIVE).build())); - } else { - // wait until active - new UnitStateAwaiter<>(this.appRemote).waitForState(data -> data.getActivationState().getValue() == ActivationStateType.ActivationState.State.ACTIVE); - } - } catch (Exception ex) { - throw ExceptionPrinter.printHistoryAndReturnThrowable(ex, LOGGER); - } - } - - @AfterEach - @Timeout(30) - public void removeAgent() throws Exception { - Registries.getUnitRegistry().removeUnitConfig(appConfig); - appRemote.waitForConnectionState(ConnectionState.State.DISCONNECTED); - } - - public abstract Class getAppClass(); - - public abstract UnitConfig.Builder getAppConfig() throws CouldNotPerformException; -} diff --git a/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.kt b/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.kt new file mode 100644 index 0000000000..f7a057767e --- /dev/null +++ b/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.kt @@ -0,0 +1,138 @@ +package org.openbase.app.test.agent + +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Timeout +import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController +import org.openbase.bco.dal.remote.layer.unit.Units +import org.openbase.bco.dal.remote.layer.unit.app.AppRemote +import org.openbase.bco.dal.remote.layer.unit.util.UnitStateAwaiter +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.extension.type.processing.LabelProcessor.addLabel +import org.openbase.type.domotic.state.ActivationStateType +import org.openbase.type.domotic.state.ConnectionStateType +import org.openbase.type.domotic.unit.UnitConfigType +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig +import org.openbase.type.domotic.unit.UnitFilterType +import org.openbase.type.domotic.unit.UnitTemplateType +import org.openbase.type.domotic.unit.app.AppClassType +import org.openbase.type.domotic.unit.app.AppDataType +import org.slf4j.LoggerFactory +import java.util.* +import java.util.concurrent.TimeUnit + +/*- +* #%L +* BCO App Test Framework +* %% +* Copyright (C) 2018 - 2021 openbase.org +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public +* License along with this program. If not, see +* . +* #L% +*/ +abstract class AbstractBCOAppManagerTest : BCOAppTest() { + protected var appClass: AppClassType.AppClass? = null + protected var appConfig: UnitConfig? = null + protected var appRemote: AppRemote? = null + protected var appController: APP_CLASS? = null + + @BeforeEach + @Timeout(30) + @Throws(Exception::class) + fun prepareAppManager() { + try { + // setup and register app class + val appClassBuilder = AppClassType.AppClass.newBuilder() + addLabel( + appClassBuilder.getLabelBuilder(), + Locale.ENGLISH, + getAppClass().getSimpleName().replace("App", "") + ) + val appClass = Registries.getClassRegistry().registerAppClass(appClassBuilder.build())[5, TimeUnit.SECONDS] + var appConfig = getAppConfig().apply { + this.appConfigBuilder.setAppClassId(appClass.getId()) + this.setUnitType(UnitTemplateType.UnitTemplate.UnitType.APP) + this.appConfigBuilder.setAutostart(true) + }.build() + + // cleanup old app instances + appConfig.aliasList + .flatMap { alias -> + Registries.getUnitRegistry().getUnitConfigs( + UnitFilterType.UnitFilter.newBuilder() + .setProperties(UnitConfig.newBuilder().addAlias(alias).build()).build() + ) + } + .distinctBy { config -> config.id } + .forEach { config -> + Registries.getUnitRegistry().removeUnitConfig(config).get( + 5, + TimeUnit.SECONDS + ) + } + + // register app + appConfig = Registries.getUnitRegistry().registerUnitConfig(appConfig)[5, TimeUnit.SECONDS] + Registries.waitUntilReady() + + // retrieve remote and activate app + val appRemote = Units.getUnit(appConfig, true, Units.APP) + + // wait until active + UnitStateAwaiter(appRemote).waitForState { data: AppDataType.AppData -> + data.activationState.value == ActivationStateType.ActivationState.State.ACTIVE + } + + this.appClass = appClass + this.appConfig = appConfig + this.appRemote = appRemote + this.appController = + appManagerLauncher.launchable!!.appControllerRegistry.get(appConfig?.getId()) as APP_CLASS + + // final sync + Registries.requestData()[5, TimeUnit.SECONDS] + } catch (ex: Exception) { + throw ExceptionPrinter.printHistoryAndReturnThrowable(ex, LOGGER) + } + } + + @AfterEach + @Timeout(30) + @Throws(Exception::class) + fun removeAgent() { + if (appConfig != null) { + Registries.getUnitRegistry().removeUnitConfig(appConfig).get() + } + + if (appRemote != null) { + appRemote!!.waitForConnectionState(ConnectionStateType.ConnectionState.State.DISCONNECTED) + } + + if (appClass != null) { + Registries.getClassRegistry().removeAppClass(appClass).get() + } + } + + abstract fun getAppClass(): Class + + @Throws(CouldNotPerformException::class) + abstract fun getAppConfig(): UnitConfigType.UnitConfig.Builder + + companion object { + private val LOGGER = LoggerFactory.getLogger(AbstractBCOAgentManagerTest::class.java) + } +} diff --git a/module/app/test/src/main/java/org/openbase/app/test/agent/BCOAppTest.java b/module/app/test/src/main/java/org/openbase/app/test/agent/BCOAppTest.java index 4e76fc78ad..2fd9c73e49 100644 --- a/module/app/test/src/main/java/org/openbase/app/test/agent/BCOAppTest.java +++ b/module/app/test/src/main/java/org/openbase/app/test/agent/BCOAppTest.java @@ -30,6 +30,7 @@ import org.openbase.bco.dal.control.layer.unit.app.AppManagerLauncher; import org.openbase.bco.dal.control.layer.unit.device.DeviceManagerLauncher; import org.openbase.bco.dal.control.layer.unit.location.LocationManagerLauncher; +import org.openbase.bco.dal.control.message.MessageManagerLauncher; import org.openbase.bco.dal.lib.layer.unit.UnitController; import org.openbase.bco.dal.test.AbstractBCOTest; import org.openbase.bco.registry.remote.Registries; @@ -46,6 +47,7 @@ public class BCOAppTest extends AbstractBCOTest { protected static AppManagerLauncher appManagerLauncher; protected static DeviceManagerLauncher deviceManagerLauncher; protected static LocationManagerLauncher locationManagerLauncher; + protected static MessageManagerLauncher messageManagerLauncher; @BeforeAll @Timeout(30) @@ -67,6 +69,10 @@ public static void setupBcoApp() throws Throwable { appManagerLauncher = new AppManagerLauncher(); appManagerLauncher.launch().get(); + LOGGER.trace("Start message manager..."); + messageManagerLauncher = new MessageManagerLauncher(); + messageManagerLauncher.launch().get(); + LOGGER.trace("Finally wait for registry..."); Registries.waitForData(); diff --git a/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOLauncher.java b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOLauncher.java index 25d5495524..4a2d932a90 100644 --- a/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOLauncher.java +++ b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOLauncher.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -32,6 +32,7 @@ import org.openbase.bco.dal.control.layer.unit.location.LocationManagerLauncher; import org.openbase.bco.dal.control.layer.unit.scene.SceneManagerLauncher; import org.openbase.bco.dal.control.layer.unit.user.UserManagerLauncher; +import org.openbase.bco.dal.control.message.MessageManagerLauncher; import org.openbase.bco.device.openhab.OpenHABDeviceManagerLauncher; import org.openbase.bco.device.openhab.registry.OpenHABConfigSynchronizerLauncher; import org.openbase.bco.device.openhab.sitemap.OpenHABSitemapSynchronizerLauncher; @@ -57,7 +58,7 @@ public static void main(final String[] args) { BCO.printLogo(); // create dynamic launcher container - ArrayList> launcher = new ArrayList<>(); + ArrayList>> launcher = new ArrayList<>(); /** * Configure Authenticator Launcher @@ -82,6 +83,7 @@ public static void main(final String[] args) { launcher.add(LocationManagerLauncher.class); launcher.add(SceneManagerLauncher.class); launcher.add(UserManagerLauncher.class); + launcher.add(MessageManagerLauncher.class); /** * API Launcher diff --git a/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCORegistryValidator.java b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCORegistryValidator.java index 27bc0d38da..3e58ab20cd 100644 --- a/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCORegistryValidator.java +++ b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCORegistryValidator.java @@ -91,8 +91,8 @@ public static void validateRegistries() throws CouldNotPerformException, Interru System.out.println("=== " + AnsiColor.colorize("Check registries" + (JPService.getValue(JPWaitForData.class, false) ? " and wait for data." : ""), AnsiColor.ANSI_BLUE) + " ===\n"); // check - final List registries = Registries.getRegistries(JPService.getValue(JPWaitForData.class, false)); - for (final RegistryRemote registry : registries) { + final List> registries = Registries.getRegistries(JPService.getValue(JPWaitForData.class, false)); + for (final RegistryRemote registry : registries) { if (!check(registry, TimeUnit.SECONDS.toMillis(2))) { // in case we should wait diff --git a/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOTestLauncher.java b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOTestLauncher.java index dbc69570fd..0e8e219702 100644 --- a/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOTestLauncher.java +++ b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOTestLauncher.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -24,22 +24,22 @@ import org.openbase.bco.api.graphql.BcoApiGraphQlLauncher; import org.openbase.bco.authentication.core.AuthenticatorLauncher; +import org.openbase.bco.authentication.lib.BCO; import org.openbase.bco.dal.control.layer.unit.agent.AgentManagerLauncher; import org.openbase.bco.dal.control.layer.unit.app.AppManagerLauncher; import org.openbase.bco.dal.control.layer.unit.device.DeviceManagerLauncher; import org.openbase.bco.dal.control.layer.unit.location.LocationManagerLauncher; import org.openbase.bco.dal.control.layer.unit.scene.SceneManagerLauncher; import org.openbase.bco.dal.control.layer.unit.user.UserManagerLauncher; +import org.openbase.bco.dal.control.message.MessageManagerLauncher; import org.openbase.bco.dal.lib.jp.JPProviderControlMode; import org.openbase.bco.registry.activity.core.ActivityRegistryLauncher; import org.openbase.bco.registry.clazz.core.ClassRegistryLauncher; -import org.openbase.bco.authentication.lib.BCO; import org.openbase.bco.registry.message.core.MessageRegistryLauncher; import org.openbase.bco.registry.template.core.TemplateRegistryLauncher; import org.openbase.bco.registry.unit.core.UnitRegistryLauncher; import org.openbase.jps.core.JPService; import org.openbase.jul.pattern.launch.AbstractLauncher; -import org.openbase.jul.pattern.launch.jp.JPPrintLauncher; /** * @author Divine Threepwood @@ -76,6 +76,7 @@ public static void main(final String[] args) { LocationManagerLauncher.class, SceneManagerLauncher.class, UserManagerLauncher.class, + MessageManagerLauncher.class, /* * API Launcher diff --git a/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/AuthenticatedServiceProcessor.java b/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/AuthenticatedServiceProcessor.java index ceffdd6060..591ef0fee8 100644 --- a/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/AuthenticatedServiceProcessor.java +++ b/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/AuthenticatedServiceProcessor.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -111,69 +111,66 @@ public static Authen final Class internalClass, final TicketValidator ticketValidator, final InternalIdentifiedProcessable executable) throws CouldNotPerformException { - try { - // start to build the response - AuthenticatedValue.Builder response = AuthenticatedValue.newBuilder(); - if (authenticatedValue.hasTicketAuthenticatorWrapper()) { - try { - if (!JPService.getProperty(JPAuthentication.class).getValue()) { - throw new CouldNotPerformException("Cannot execute authenticated action because authentication is disabled"); - } - } catch (JPNotAvailableException ex) { - throw new CouldNotPerformException("Could not check JPEnableAuthentication property", ex); - } - // verify ticket in encrypt tokens if they were send - final AuthenticationBaseData authenticationBaseData = ticketValidator.verifyClientServerTicket(authenticatedValue); - RECEIVE decrypted = null; - // decrypt the send type from the AuthenticatedValue - if (authenticatedValue.hasValue()) { - decrypted = EncryptionHelper.decryptSymmetric(authenticatedValue.getValue(), authenticationBaseData.getSessionKey(), internalClass); + AuthenticatedValue.Builder response = AuthenticatedValue.newBuilder(); + if (authenticatedValue.hasTicketAuthenticatorWrapper()) { + try { + if (!JPService.getProperty(JPAuthentication.class).getValue()) { + throw new CouldNotPerformException("Cannot execute authenticated action because authentication is disabled"); } + } catch (JPNotAvailableException ex) { + throw new CouldNotPerformException("Could not check JPEnableAuthentication property", ex); + } + // verify ticket in encrypt tokens if they were send + final AuthenticationBaseData authenticationBaseData = ticketValidator.verifyClientServerTicket(authenticatedValue); - // execute the action of the server - RETURN result = executable.process(decrypted, authenticationBaseData); + RECEIVE decrypted = null; + // decrypt the send type from the AuthenticatedValue + if (authenticatedValue.hasValue()) { + decrypted = EncryptionHelper.decryptSymmetric(authenticatedValue.getValue(), authenticationBaseData.getSessionKey(), internalClass); + } - if (result != null) { - // encrypt the result and add it to the response - response.setValue(EncryptionHelper.encryptSymmetric(result, authenticationBaseData.getSessionKey())); - } - // add updated ticket to response - response.setTicketAuthenticatorWrapper(authenticationBaseData.getTicketAuthenticatorWrapper()); - } else { - // ticket no available so request without login - try { - RECEIVE message = null; + // execute the action of the server + RETURN result = executable.process(decrypted, authenticationBaseData); - if (authenticatedValue.hasValue() && !authenticatedValue.getValue().isEmpty()) { - if (!Message.class.isAssignableFrom(internalClass)) { - throw new CouldNotPerformException("Authenticated value has a value but the method implemented by the server did not expect one!"); - } - // when not logged in the received value is not encrypted but just send as a byte string - // so get the received message by calling parseFrom which is supported by every message - Method parseFrom = internalClass.getMethod("parseFrom", ByteString.class); - message = (RECEIVE) parseFrom.invoke(null, authenticatedValue.getValue()); - } + if (result != null) { + // encrypt the result and add it to the response + response.setValue(EncryptionHelper.encryptSymmetric(result, authenticationBaseData.getSessionKey())); + } + // add updated ticket to response + response.setTicketAuthenticatorWrapper(authenticationBaseData.getTicketAuthenticatorWrapper()); + } else { + // ticket no available so request without login + try { + RECEIVE message = null; - // execute the action of the server - RETURN result = executable.process(message, null); - if (result != null) { - if (!(result instanceof Message)) { - throw new CouldNotPerformException("Result[" + result + "] of authenticated action is not a message or not null and therefore not supported!"); - } + if (authenticatedValue.hasValue() && !authenticatedValue.getValue().isEmpty()) { + if (!Message.class.isAssignableFrom(internalClass)) { + throw new CouldNotPerformException("Authenticated value has a value but the method implemented by the server did not expect one!"); + } + // when not logged in the received value is not encrypted but just send as a byte string + // so get the received message by calling parseFrom which is supported by every message + Method parseFrom = internalClass.getMethod("parseFrom", ByteString.class); + message = (RECEIVE) parseFrom.invoke(null, authenticatedValue.getValue()); + } - // add result as a byte string to the response - response.setValue(((Message) result).toByteString()); + // execute the action of the server + RETURN result = executable.process(message, null); + if (result != null) { + if (!(result instanceof Message)) { + throw new CouldNotPerformException("Result[" + result + "] of authenticated action is not a message or not null and therefore not supported!"); } - } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { - throw new CouldNotPerformException("Could not invoke parseFrom method on [" + internalClass.getSimpleName() + "]", ex); + + // add result as a byte string to the response + response.setValue(((Message) result).toByteString()); } + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | + InvocationTargetException ex) { + throw new CouldNotPerformException("Could not invoke parseFrom method on [" + internalClass.getSimpleName() + "]", ex); } - // return the response - return response.build(); - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not execute authenticated action!", ex); } + // return the response + return response.build(); } /** diff --git a/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/CachedAuthenticationRemote.java b/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/CachedAuthenticationRemote.java index d61d0b8a25..525c26bebe 100644 --- a/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/CachedAuthenticationRemote.java +++ b/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/CachedAuthenticationRemote.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -23,7 +23,10 @@ */ import org.openbase.jps.core.JPService; -import org.openbase.jul.exception.*; +import org.openbase.jul.exception.CouldNotPerformException; +import org.openbase.jul.exception.ExceptionProcessor; +import org.openbase.jul.exception.NotAvailableException; +import org.openbase.jul.exception.ShutdownInProgressException; import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.iface.Shutdownable; import org.openbase.jul.schedule.SyncObject; @@ -40,7 +43,7 @@ public class CachedAuthenticationRemote { private static AuthenticationRemote authenticationRemote; private static volatile boolean shutdown = false; private static final SyncObject REMOTE_LOCK = new SyncObject("CachedAuthenticationRemote"); - private static final SyncObject REGISTY_LOCK = new SyncObject("RegistyLock"); + private static final SyncObject REGISTRY_LOCK = new SyncObject("RegistryLock"); /** * Setup shutdown hook @@ -76,7 +79,7 @@ public static AuthenticationRemote getRemote() throws NotAvailableException { return authenticationRemote; } - synchronized (REGISTY_LOCK) { + synchronized (REGISTRY_LOCK) { if (authenticationRemote == null) { try { authenticationRemote = new AuthenticationRemote(); diff --git a/module/authentication/test/src/test/java/org/openbase/bco/authentication/test/AuthenticatorControllerTest.java b/module/authentication/test/src/test/java/org/openbase/bco/authentication/test/AuthenticatorControllerTest.java index 2ef2a61f6d..474b62cbb5 100644 --- a/module/authentication/test/src/test/java/org/openbase/bco/authentication/test/AuthenticatorControllerTest.java +++ b/module/authentication/test/src/test/java/org/openbase/bco/authentication/test/AuthenticatorControllerTest.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -42,12 +42,11 @@ import org.openbase.type.domotic.authentication.TicketAuthenticatorWrapperType.TicketAuthenticatorWrapper; import org.openbase.type.domotic.authentication.TicketSessionKeyWrapperType.TicketSessionKeyWrapper; import org.openbase.type.domotic.authentication.UserClientPairType.UserClientPair; -import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutionException; -import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * @author Tamino Huxohl @@ -334,7 +333,7 @@ public void testLoginCombinations() throws Exception { CachedAuthenticationRemote.getRemote().requestTicketGrantingTicket(clientAsymmetricUserAsymmetric).get(); fail("No exception throw even when authentication method is not supported."); } catch (ExecutionException ex) { - assertTrue(ExceptionProcessor.getInitialCauseMessage(ex).contains("NotSupportedException")); + assertTrue(ExceptionProcessor.getInitialCause(ex).getClass().getSimpleName().contains("NotSupportedException")); } finally { ExceptionPrinter.setBeQuit(false); } diff --git a/module/authentication/test/src/test/java/org/openbase/bco/authentication/test/StayLoggedInTest.kt b/module/authentication/test/src/test/java/org/openbase/bco/authentication/test/StayLoggedInTest.kt index 2c677d4b09..d0437c236c 100644 --- a/module/authentication/test/src/test/java/org/openbase/bco/authentication/test/StayLoggedInTest.kt +++ b/module/authentication/test/src/test/java/org/openbase/bco/authentication/test/StayLoggedInTest.kt @@ -13,7 +13,7 @@ import org.openbase.bco.authentication.mock.MockClientStore import org.openbase.bco.authentication.mock.MockCredentialStore import org.openbase.jps.core.JPService import org.openbase.jul.communication.mqtt.test.MqttIntegrationTest -import org.openbase.jul.exception.initialCauseMessage +import org.openbase.jul.exception.initialCause import org.openbase.jul.exception.printer.ExceptionPrinter import java.util.concurrent.ExecutionException @@ -93,7 +93,7 @@ class StayLoggedInTest : MqttIntegrationTest() { CachedAuthenticationRemote.getRemote().validateClientServerTicket(wrapper).get() Assertions.fail("No exception thrown even though the session should have timed out") } catch (ex: ExecutionException) { - ex.initialCauseMessage shouldContain "SessionExpired" + ex.initialCause.javaClass.simpleName shouldContain "SessionExpired" } finally { ExceptionPrinter.setBeQuit(false) } diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractAuthorizedBaseUnitController.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractAuthorizedBaseUnitController.java index 8b63a196e5..674639d4c5 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractAuthorizedBaseUnitController.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractAuthorizedBaseUnitController.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -63,7 +63,9 @@ public abstract class AbstractAuthorizedBaseUnitController observedTaskList; + protected UnitConfig userConfig = null; + + private final ArrayList observedTaskList; private final static ProtoBufJSonProcessor protoBufJSonProcessor = new ProtoBufJSonProcessor(); @@ -93,10 +95,10 @@ public UnitConfig applyConfigUpdate(final UnitConfig config) throws CouldNotPerf private AuthToken requestAuthToken(final UnitConfig unitConfig) throws CouldNotPerformException, InterruptedException { try { - final UnitConfig userUnitConfig = UnitUserCreationPlugin.findUser(unitConfig.getId(), Registries.getUnitRegistry().getUnitConfigsByUnitType(UnitType.USER)); - final AuthenticationToken authenticationToken = AuthenticationToken.newBuilder().setUserId(userUnitConfig.getId()).build(); + userConfig = UnitUserCreationPlugin.findUser(unitConfig.getId(), Registries.getUnitRegistry(true).getUnitConfigsByUnitType(UnitType.USER)); + final AuthenticationToken authenticationToken = AuthenticationToken.newBuilder().setUserId(userConfig.getId()).build(); final SessionManager sessionManager = new SessionManager(); - sessionManager.loginUser(userUnitConfig.getId(), true); + sessionManager.loginUser(userConfig.getId(), true); final AuthenticatedValue authenticatedValue = sessionManager.initializeRequest(authenticationToken, null); return AuthToken.newBuilder().setAuthenticationToken(new AuthenticatedValueFuture<>( Registries.getUnitRegistry().requestAuthenticationTokenAuthenticated(authenticatedValue), @@ -104,6 +106,7 @@ private AuthToken requestAuthToken(final UnitConfig unitConfig) throws CouldNotP authenticatedValue.getTicketAuthenticatorWrapper(), sessionManager).get()).build(); } catch (CouldNotPerformException | ExecutionException ex) { + logger.error("Could not create authentication token for " + this + " " + unitConfig.getId()); throw new CouldNotPerformException("Could not create authentication token for " + this + " " + UnitConfigProcessor.getDefaultAlias(unitConfig, unitConfig.getId()), ex); } } @@ -149,6 +152,7 @@ protected AuthToken getToken() { * Additionally, the auth token of this controller is passed to the remote action and the action auto extension routine is enabled. * * @param futureAction used to identify the action to observe. + * * @return a ready to use action remote instance. */ protected RemoteAction observe(final Future futureAction) { @@ -166,7 +170,7 @@ protected RemoteAction observe(final Future futureAction) { // register new action observedTaskList.add(remoteAction); - // validate and initiate forced stop if instance generates to many messages. + // validate and initiate forced stop if instance generates too many messages. validateUnitOverload(); return remoteAction; @@ -196,7 +200,7 @@ private synchronized void validateUnitOverload() { } if (eventsPerHour > MAX_ACTIN_SUBMITTION_PER_MINUTE * 60) { - logger.error(this + " generates to many actions and will be terminated!"); + logger.error(this + " generates too many actions and will be terminated!"); try { applyServiceState(ActivationState.newBuilder().setValue(State.INACTIVE), ServiceType.ACTIVATION_STATE_SERVICE); } catch (CouldNotPerformException ex) { diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractUnitController.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractUnitController.java index a13c39b949..3164d573d3 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractUnitController.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractUnitController.java @@ -186,7 +186,7 @@ public void update(DataProvider source, UnitRegistryData data) } } catch (NotAvailableException ex) { // unit config has been removed, probably because of deletion and a higher controller will do the shutdown in this case - logger.debug("Could not update unit controller", ex); + logger.trace("Skip controller config update since unit is not yet or not anymore included in the registry."); } catch (CouldNotPerformException ex) { ExceptionPrinter.printHistory("Could not update unit config of " + this, ex, logger); } diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/InfluxDbProcessor.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/InfluxDbProcessor.java index 22432b8452..93f10865dd 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/InfluxDbProcessor.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/InfluxDbProcessor.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -78,7 +78,7 @@ public class InfluxDbProcessor { public static final String INFLUXDB_BATCH_LIMIT = "INFLUXDB_BATCH_LIMIT"; public static final String INFLUXDB_BATCH_LIMIT_DEFAULT = "100"; public static final String INFLUXDB_URL = "INFLUXDB_URL"; - public static final String INFLUXDB_URL_DEFAULT = "http://localhost:8086"; + public static final String INFLUXDB_URL_DEFAULT = "http://influxdb:8086"; public static final String INFLUXDB_ORG = "INFLUXDB_ORG"; public static final String INFLUXDB_ORG_DEFAULT = "openbase"; public static final String INFLUXDB_TOKEN = "INFLUXDB_TOKEN"; @@ -458,7 +458,7 @@ public static Future queryAggregatedServiceState(final Q return FutureProcessor.completedFuture(newAggregatedServiceState); } catch (CouldNotPerformException ex) { - return FutureProcessor.canceledFuture(AggregatedServiceState.class,new CouldNotPerformException("Could not query aggregated service state", ex)); + return FutureProcessor.canceledFuture(AggregatedServiceState.class, new CouldNotPerformException("Could not query aggregated service state", ex)); } } } diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java index 355ec29991..9c96835574 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java @@ -10,35 +10,35 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . * #L% */ + import org.openbase.bco.dal.lib.layer.unit.app.App; import org.openbase.bco.dal.lib.layer.unit.app.AppController; import org.openbase.bco.dal.lib.layer.unit.app.AppControllerFactory; import org.openbase.bco.registry.remote.Registries; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.NotAvailableException; +import org.openbase.jul.extension.type.processing.LabelProcessor; import org.openbase.jul.processing.StringProcessor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; import org.openbase.type.domotic.unit.app.AppClassType.AppClass; -import org.openbase.jul.extension.type.processing.LabelProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; import java.util.Locale; /** - * * @author Tamino Huxohl */ public class AppControllerFactoryImpl implements AppControllerFactory { @@ -73,9 +73,11 @@ public AppController newInstance(final UnitConfig appUnitConfig) throws org.open try { // try to load preset app String className = PRESET_APP_PACKAGE_PREFIX - + "." + LabelProcessor.getLabelByLanguage(Locale.ENGLISH, appClass.getLabel()) + "App"; + + "." + StringProcessor.removeWhiteSpaces(LabelProcessor.getLabelByLanguage(Locale.ENGLISH, appClass.getLabel())) + "App"; app = (AppController) Thread.currentThread().getContextClassLoader().loadClass(className).getConstructor().newInstance(); - } catch (CouldNotPerformException | ClassNotFoundException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | NoSuchMethodException | InvocationTargetException ex) { + } catch (CouldNotPerformException | ClassNotFoundException | SecurityException | InstantiationException | + IllegalAccessException | IllegalArgumentException | NoSuchMethodException | + InvocationTargetException ex) { // try to load custom app String className = CUSTOM_APP_PACKAGE_PREFIX + "." + StringProcessor.removeWhiteSpaces(LabelProcessor.getLabelByLanguage(Locale.ENGLISH, appClass.getLabel())).toLowerCase() @@ -84,7 +86,9 @@ public AppController newInstance(final UnitConfig appUnitConfig) throws org.open } logger.debug("Creating app of type [" + LabelProcessor.getBestMatch(appClass.getLabel()) + "]"); app.init(appUnitConfig); - } catch (CouldNotPerformException | ClassNotFoundException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InterruptedException | NoSuchMethodException | InvocationTargetException ex) { + } catch (CouldNotPerformException | ClassNotFoundException | SecurityException | InstantiationException | + IllegalAccessException | IllegalArgumentException | InterruptedException | NoSuchMethodException | + InvocationTargetException ex) { throw new org.openbase.jul.exception.InstantiationException(App.class, appUnitConfig.getId(), ex); } return app; diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/scene/SceneControllerImpl.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/scene/SceneControllerImpl.java index 8062e2903a..817ddd693c 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/scene/SceneControllerImpl.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/scene/SceneControllerImpl.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -55,6 +55,7 @@ import org.openbase.jul.schedule.TimeoutSplitter; import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; import org.openbase.type.domotic.action.ActionParameterType.ActionParameter; +import org.openbase.type.domotic.action.ActionPriorityType.ActionPriority.Priority; import org.openbase.type.domotic.action.ActionReferenceType.ActionReference; import org.openbase.type.domotic.authentication.AuthenticatedValueType.AuthenticatedValue; import org.openbase.type.domotic.service.ServiceStateDescriptionType.ServiceStateDescription; @@ -72,6 +73,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.time.Duration; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.CancellationException; @@ -195,7 +197,8 @@ public UnitConfig applyConfigUpdate(UnitConfig config) throws CouldNotPerformExc final ActionParameter actionParameterPrototype = ActionParameter.newBuilder() .setInterruptible(true) .setSchedulable(true) - .setExecutionTimePeriod(Long.MAX_VALUE).build(); + .setPriority(Priority.HIGH) + .setExecutionTimePeriod(Duration.ofMinutes(30).toMillis()).build(); requiredActionPool.initViaServiceStateDescription(config.getSceneConfig().getRequiredServiceStateDescriptionList(), actionParameterPrototype, () -> getActivationState().getValue() == ActivationState.State.ACTIVE); optionalActionPool.initViaServiceStateDescription(config.getSceneConfig().getOptionalServiceStateDescriptionList(), actionParameterPrototype, () -> getActivationState().getValue() == ActivationState.State.ACTIVE); return config; @@ -272,7 +275,7 @@ public synchronized Future setActivationState(final Activatio // shutdown all existing action observer to not let old observation interfere with new activations. - if(actionObserver != null) { + if (actionObserver != null) { actionObserver.shutdown(); } @@ -436,7 +439,7 @@ private RequiredActionObserver(final List requiredActionImpact, private void verifyAllStates() { try { - logger.trace(() -> "verify "+unitAndRequiredServiceStateMap.entrySet().size()+ " states of "+ getLabel("?")); + logger.trace(() -> "verify " + unitAndRequiredServiceStateMap.entrySet().size() + " states of " + getLabel("?")); for (Entry, RequiredServiceDescription> unitActionReferenceEntry : unitAndRequiredServiceStateMap.entrySet()) { try { // skip unit in case its offline, since then the verification is automatically @@ -457,7 +460,7 @@ private void verifyAllStates() { private void verifyState(final ServiceProvider unit, final Message serviceState) throws VerificationFailedException { // skip verification on destroyed required action observer! - if(destroy) { + if (destroy) { return; } @@ -466,13 +469,13 @@ private void verifyState(final ServiceProvider unit, final Me } // skip in case no service state was delivered - if(serviceState.toString().isBlank()) { + if (serviceState.toString().isBlank()) { return; } if (!Services.equalServiceStates(unitAndRequiredServiceStateMap.get(unit).getServiceState(), serviceState)) { logger.trace(() -> unitAndRequiredServiceStateMap.get(unit).getServiceState() + " is not equals " + serviceState.toString().substring(0, 20) + " and will cancel: " + SceneControllerImpl.this.getLabel("?")); - if(Actions.validateInitialAction(serviceState)) { + if (Actions.validateInitialAction(serviceState)) { throw new VerificationFailedException("State of " + unit + "not meet!"); } } diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManager.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManager.kt new file mode 100644 index 0000000000..1b26e483d1 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManager.kt @@ -0,0 +1,109 @@ +package org.openbase.bco.dal.control.message + +import com.google.protobuf.Message +import org.openbase.bco.dal.lib.layer.service.Services +import org.openbase.bco.dal.lib.layer.unit.UnitRemote +import org.openbase.bco.dal.remote.layer.unit.Units +import org.openbase.bco.registry.message.remote.removeUserMessageAuthenticated +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.InitializationException +import org.openbase.jul.iface.Launchable +import org.openbase.jul.iface.VoidInitializable +import org.openbase.jul.pattern.Observer +import org.openbase.jul.pattern.provider.DataProvider +import org.openbase.jul.schedule.RecurrenceEventFilter +import org.openbase.type.domotic.authentication.AuthTokenType.AuthToken +import org.openbase.type.domotic.registry.MessageRegistryDataType.MessageRegistryData +import org.slf4j.LoggerFactory +import java.time.Duration +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.write + +class MessageManager : Launchable, VoidInitializable { + + private var logger = LoggerFactory.getLogger(MessageManager::class.java) + + private val unitsOfConditionsLock = ReentrantReadWriteLock() + + private val maxUpdateInterval: Duration = Duration.ofSeconds(30) + + private var active = false + + private val removeOutdatedMessagesTask: RecurrenceEventFilter = + object : RecurrenceEventFilter(maxUpdateInterval.toMillis()) { + override fun relay() { + removeOutdatedMessages() + } + } + + fun removeOutdatedMessages(auth: AuthToken? = null) { + logger.trace("removeOutdatedMessages") + Registries.getMessageRegistry().userMessages + .filterNot { message -> + message.conditionList.any { condition -> + Units.getUnit(condition.unitId, true).let { unit -> + Services.equalServiceStates( + unit.getServiceState(condition.serviceType), + Services.deserializeServiceState(condition) + ) + } + } + } + .forEach { + if (auth != null) { + Registries.getMessageRegistry().removeUserMessageAuthenticated(it, auth).get(5, TimeUnit.SECONDS) + } else { + Registries.getMessageRegistry().removeUserMessage(it).get(5, TimeUnit.SECONDS) + } + } + } + + private var unitsOfConditions: List>? = null + + private val conditionObserver: Observer, Message> = + Observer { _, _ -> removeOutdatedMessagesTask.trigger() } + + private val messageRegistryChangeObserver: Observer, MessageRegistryData> = + Observer { _, data -> + unitsOfConditionsLock.write { + unitsOfConditions?.forEach { it.removeDataObserver(conditionObserver) } + unitsOfConditions = data.userMessageList.let { userMessages -> + userMessages.flatMap { it.conditionList } + .map { it.unitId } + .distinct() + .mapNotNull { unitId -> + try { + Units.getUnit(unitId, false) + } catch (e: CouldNotPerformException) { + logger.warn("Could not resolve unit with id $unitId", e) + null + } + } + } + unitsOfConditions?.forEach { it.addDataObserver(conditionObserver) } + } + } + + @Throws(InitializationException::class, InterruptedException::class) + override fun init() { + // this overwrite is needed to overwrite the default implementation! + } + + override fun activate() { + active = true + Registries.getMessageRegistry().addDataObserver(messageRegistryChangeObserver) + } + + override fun deactivate() { + Registries.getMessageRegistry().removeDataObserver(messageRegistryChangeObserver) + unitsOfConditionsLock.write { + unitsOfConditions?.forEach { it.removeDataObserver(conditionObserver) } + unitsOfConditions = null + } + active = false + } + + override fun isActive(): Boolean = active +} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManagerLauncher.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManagerLauncher.kt new file mode 100644 index 0000000000..8f0cf54aa9 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManagerLauncher.kt @@ -0,0 +1,32 @@ +package org.openbase.bco.dal.control.message + +import org.openbase.bco.authentication.lib.BCO +import org.openbase.bco.authentication.lib.jp.JPBCODistributionDirectory +import org.openbase.bco.dal.lib.jp.JPBenchmarkMode +import org.openbase.bco.dal.lib.jp.JPHardwareSimulationMode +import org.openbase.bco.dal.lib.jp.JPProviderControlMode +import org.openbase.jps.core.JPService +import org.openbase.jul.pattern.launch.AbstractLauncher + +class MessageManagerLauncher : + AbstractLauncher(MessageManager::class.java, MessageManager::class.java) { + public override fun loadProperties() { + JPService.registerProperty(JPBCODistributionDirectory::class.java) + JPService.registerProperty(JPHardwareSimulationMode::class.java) + JPService.registerProperty(JPBenchmarkMode::class.java) + JPService.registerProperty(JPProviderControlMode::class.java) + } + + companion object { + @Throws(Throwable::class) + @JvmStatic + fun main(args: Array) { + BCO.printLogo() + main( + BCO::class.java, + MessageManager::class.java, args, + MessageManagerLauncher::class.java + ) + } + } +} diff --git a/module/dal/example/src/main/java/org/openbase/bco/dal/example/HowToObserveMotionStatesOfAllRooms.java b/module/dal/example/src/main/java/org/openbase/bco/dal/example/HowToObserveMotionStatesOfAllRooms.java index c0c4fabb29..98b932558f 100644 --- a/module/dal/example/src/main/java/org/openbase/bco/dal/example/HowToObserveMotionStatesOfAllRooms.java +++ b/module/dal/example/src/main/java/org/openbase/bco/dal/example/HowToObserveMotionStatesOfAllRooms.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -48,10 +48,9 @@ /** - * * This howto demonstrates how all rooms of your smart environment can be observed regarding their current motion state. * BCO offers two different approaches that are both addressed within this howto by EXAMPLE 1 and EXAMPLE 2 - * + *

* Note: You can use the PRESENCE_STATE service as well to rely not only on motion but also on other sensor events to detect a human presence at locations. * Note: This howto requires a running bco platform provided by your network. * @@ -70,17 +69,17 @@ public static void howto() throws InterruptedException { LOGGER.info("authenticate current session..."); BCOLogin.getSession().loginUserViaUsername("admin", "admin", false); - // EXAMPLE 1: observe the movement in all rooms via a custom unit pool. + // EXAMPLE 1: observe the movement in all rooms via a custom unit pool. // create a new unit pool which is mainly a collection of unit remotes - final CustomUnitPool locationPool = new CustomUnitPool(); + final CustomUnitPool locationPool = new CustomUnitPool>(); // make sure the pool only contains tile locations (rooms are represented as tiles in bco). // so we want to filter all non locations and non tiles. locationPool.init( unitConfig -> unitConfig.getUnitType() == UnitType.LOCATION, unitConfig -> unitConfig.getLocationConfig().getLocationType() == LocationType.TILE - ); + ); // activate the pool so units get synchronized... locationPool.activate(); @@ -89,7 +88,7 @@ public static void howto() throws InterruptedException { locationPool.addServiceStateObserver((source, data) -> { // filter non motion state events - if(source.getServiceType() != ServiceType.MOTION_STATE_SERVICE) { + if (source.getServiceType() != ServiceType.MOTION_STATE_SERVICE) { return; } @@ -98,7 +97,7 @@ public static void howto() throws InterruptedException { final LocationRemote locationRemote = (LocationRemote) source.getServiceProvider(); // inform about the update. The location unit is delivered via the service provider method. - LOGGER.info("EXAMPLE 1: "+LabelProcessor.getBestMatch(locationRemote.getConfig().getLabel(), "?") + " has changed its motion state to " + motionState.getValue().name()); + LOGGER.info("EXAMPLE 1: " + LabelProcessor.getBestMatch(locationRemote.getConfig().getLabel(), "?") + " has changed its motion state to " + motionState.getValue().name()); }); // print a summary about the current movement state @@ -108,10 +107,10 @@ public static void howto() throws InterruptedException { // we need to wait for the remote synchronisation when accessing any unit state at the first time location.waitForData(); - LOGGER.info("EXAMPLE 1: "+location.getLabel("?") + " has currently "+ location.getMotionState().getValue().name()); + LOGGER.info("EXAMPLE 1: " + location.getLabel("?") + " has currently " + location.getMotionState().getValue().name()); } - // EXAMPLE 2: observe the movement in all rooms via location remotes + // EXAMPLE 2: observe the movement in all rooms via location remotes // query all tiles via the registry (rooms are represented as tiles in bco). final List locationConfigs = Registries.getUnitRegistry().getLocationUnitConfigsByLocationType(LocationType.TILE); @@ -127,7 +126,7 @@ public static void howto() throws InterruptedException { location.addServiceStateObserver(ServiceTempus.CURRENT, ServiceType.MOTION_STATE_SERVICE, (source, data) -> { // we know its a motion state final MotionState motionState = (MotionState) data; - LOGGER.info("EXAMPLE 2: "+location.getLabel("?") + " has changed its motion state to " + motionState.getValue().name()); + LOGGER.info("EXAMPLE 2: " + location.getLabel("?") + " has changed its motion state to " + motionState.getValue().name()); }); } @@ -135,7 +134,7 @@ public static void howto() throws InterruptedException { for (LocationRemote location : locations) { // we need to wait for the remote synchronisation when accessing any unit state at the first time location.waitForData(); - LOGGER.info("EXAMPLE 2: "+location.getLabel("?") + " has currently "+ location.getMotionState().getValue().name()); + LOGGER.info("EXAMPLE 2: " + location.getLabel("?") + " has currently " + location.getMotionState().getValue().name()); } LOGGER.info("Observe changes for 2 minutes"); diff --git a/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/ServiceStateProcessor.java b/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/ServiceStateProcessor.java index 2c2a900730..53edebebef 100644 --- a/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/ServiceStateProcessor.java +++ b/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/ServiceStateProcessor.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -34,6 +34,7 @@ import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.FatalImplementationErrorException; import org.openbase.jul.exception.NotAvailableException; +import org.openbase.jul.exception.VerificationFailedException; import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.extension.protobuf.ProtoBufBuilderProcessor; import org.openbase.jul.extension.protobuf.processing.ProtoBufFieldProcessor; @@ -48,7 +49,10 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; -import java.util.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.function.Supplier; public class ServiceStateProcessor { @@ -181,7 +185,8 @@ public static void updateLatestValueOccurrence(final EnumValueDescriptor enumVal entryMessageBuilder.setField(valueDescriptor, timestamp); entryMessageBuilder.setField(keyDescriptor, enumValueDescriptor); entryMessage = entryMessageBuilder.build(); - } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | ClassCastException ex) { + } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | + ClassCastException ex) { throw new CouldNotPerformException("Could not build service state entry!", ex); } @@ -317,7 +322,7 @@ private static Set computeActionImpact(final ServiceStateDesc // handle termination: // make sure duplicated service states are only processed ones. - if(processedServiceStates.contains(serviceStateDescription)) { + if (processedServiceStates.contains(serviceStateDescription)) { return Collections.emptySet(); } else { processedServiceStates.add(serviceStateDescription); @@ -377,7 +382,7 @@ private static Set computeActionImpact(final ServiceStateDesc } // register location itself as impact in case it's affected by child units through aggregation - if(!locationChildUnits.isEmpty()) { + if (!locationChildUnits.isEmpty()) { actionImpactList.add(buildActionImpact(serviceStateDescription).setIntermediary(true).build()); } break; @@ -440,6 +445,15 @@ public static String serializeServiceState(final Message serviceState, final boo } public static Message deserializeServiceState(final ServiceStateDescriptionOrBuilder serviceStateDescriptionOrBuilder) throws CouldNotPerformException { + + if (!serviceStateDescriptionOrBuilder.hasServiceState() || serviceStateDescriptionOrBuilder.getServiceState().isEmpty()) { + throw new VerificationFailedException("ServiceStateDescription does not provide any service state: " + serviceStateDescriptionOrBuilder); + } + + if (!serviceStateDescriptionOrBuilder.hasServiceStateClassName() || serviceStateDescriptionOrBuilder.getServiceStateClassName().isEmpty()) { + throw new VerificationFailedException("ServiceStateDescription does not provide any service state class name: " + serviceStateDescriptionOrBuilder); + } + return deserializeServiceState(serviceStateDescriptionOrBuilder.getServiceState(), serviceStateDescriptionOrBuilder.getServiceStateClassName()); } diff --git a/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/state/States.java b/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/state/States.java index a1ea493e92..ba0733bab1 100644 --- a/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/state/States.java +++ b/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/state/States.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -23,8 +23,7 @@ */ import org.openbase.type.domotic.state.ActivationStateType.ActivationState; -import org.openbase.type.domotic.state.ActivationStateType.ActivationState.State; -import org.openbase.type.domotic.state.BlindStateType; +import org.openbase.type.domotic.state.BatteryStateType.BatteryState; import org.openbase.type.domotic.state.BlindStateType.BlindState; import org.openbase.type.domotic.state.BrightnessStateType.BrightnessState; import org.openbase.type.domotic.state.ColorStateType.ColorState; @@ -33,7 +32,6 @@ import org.openbase.type.domotic.state.MotionStateType.MotionState; import org.openbase.type.domotic.state.PowerStateType.PowerState; import org.openbase.type.vision.ColorType; -import org.openbase.type.vision.ColorType.Color; import org.openbase.type.vision.ColorType.Color.Type; import org.openbase.type.vision.HSBColorType.HSBColor; @@ -125,4 +123,14 @@ public static class Contact { public static final ContactState OPEN = ContactState.newBuilder().setValue(ContactState.State.OPEN).build(); public static final ContactState CLOSED = ContactState.newBuilder().setValue(ContactState.State.CLOSED).build(); } + + /** + * Battery State Prototypes + */ + public static class Battery { + public static final BatteryState OK = BatteryState.newBuilder().setValue(BatteryState.State.OK).build(); + public static final BatteryState LOW = BatteryState.newBuilder().setValue(BatteryState.State.LOW).build(); + public static final BatteryState CRITICAL = BatteryState.newBuilder().setValue(BatteryState.State.CRITICAL).build(); + public static final BatteryState INSUFFICIENT = BatteryState.newBuilder().setValue(BatteryState.State.INSUFFICIENT).build(); + } } diff --git a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/detector/PresenceDetector.java b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/detector/PresenceDetector.java index 51e86b5bec..9fc1aa24ba 100644 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/detector/PresenceDetector.java +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/detector/PresenceDetector.java @@ -74,8 +74,8 @@ public class PresenceDetector implements Manageable, DataProvider, LocationData> locationDataObserver; private Location location; private final ObservableImpl, PresenceState> presenceStateObservable; - private final CustomUnitPool buttonUnitPool; - private final CustomUnitPool connectionUnitPool; + private final CustomUnitPool buttonUnitPool; + private final CustomUnitPool connectionUnitPool; private boolean active; private boolean shutdownInitiated = false; diff --git a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/service/AbstractServiceRemote.java b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/service/AbstractServiceRemote.java index f6dcb0f135..e43815ed42 100644 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/service/AbstractServiceRemote.java +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/service/AbstractServiceRemote.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -144,7 +144,7 @@ public AbstractServiceRemote(final ServiceType serviceType, final Class serv try { updateServiceState(); } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Initial service state computation failed. This can be the case if any required date is not available yet.", ex, logger, LogLevel.DEBUG); + ExceptionPrinter.printHistory("Service state computation failed. This can be the case if any required date is not available yet.", ex, logger, LogLevel.DEBUG); } }; this.unitConfigObserver = (source, data) -> { @@ -198,10 +198,10 @@ private void updateServiceState() throws CouldNotPerformException { @Override public ST getData() throws NotAvailableException { // synchronized (syncObject) { - if (!serviceStateObservable.isValueAvailable()) { - throw new NotAvailableException("Data"); - } - return serviceStateObservable.getValue(); + if (!serviceStateObservable.isValueAvailable()) { + throw new NotAvailableException("Data"); + } + return serviceStateObservable.getValue(); // } } @@ -487,10 +487,13 @@ public void activate() throws CouldNotPerformException, InterruptedException { remote.addConnectionStateObserver(connectionStateObserver); } + // trigger initial state computation in case data is already available try { - updateServiceState(); + if (unitRemoteMap.values().stream().anyMatch(DataProvider::isDataAvailable)) { + updateServiceState(); + } } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Initial service state computation failed. This can be the case if any required date is not available yet.", ex, logger, LogLevel.DEBUG); + logger.trace("Initial service state computation skipped because no related data available yet!"); } } diff --git a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/service/ColorStateServiceRemote.java b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/service/ColorStateServiceRemote.java index 33808fc0fb..c0d506901d 100644 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/service/ColorStateServiceRemote.java +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/service/ColorStateServiceRemote.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -26,7 +26,6 @@ import org.openbase.bco.dal.lib.layer.service.Services; import org.openbase.bco.dal.lib.layer.service.collection.ColorStateOperationServiceCollection; import org.openbase.bco.dal.lib.layer.service.operation.ColorStateOperationService; -import org.openbase.bco.dal.lib.layer.unit.Unit; import org.openbase.bco.dal.lib.layer.unit.UnitRemote; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.CouldNotTransformException; @@ -53,7 +52,6 @@ import java.util.concurrent.TimeUnit; /** - * * * @author Tamino Huxohl */ public class ColorStateServiceRemote extends AbstractServiceRemote implements ColorStateOperationServiceCollection { @@ -67,6 +65,7 @@ public ColorStateServiceRemote() { * Computes the average RGB color. * * @return {@inheritDoc} + * * @throws CouldNotPerformException {@inheritDoc} */ @Override @@ -93,7 +92,7 @@ public ColorState getColorState(final UnitType unitType) throws NotAvailableExce long timestamp = 0; ActionDescription latestAction = null; final Collection colorStateOperationServiceCollection = getServices(unitType); - int amount = colorStateOperationServiceCollection.size(); + int number = colorStateOperationServiceCollection.size(); // iterate over all services and collect available states for (ColorStateOperationService service : colorStateOperationServiceCollection) { @@ -105,7 +104,7 @@ public ColorState getColorState(final UnitType unitType) throws NotAvailableExce || !state.getColor().getHsbColor().hasHue() || !state.getColor().getHsbColor().hasSaturation() || !state.getColor().getHsbColor().hasBrightness()) { - amount--; + number--; continue; } @@ -119,14 +118,14 @@ public ColorState getColorState(final UnitType unitType) throws NotAvailableExce latestAction = selectLatestAction(state, latestAction); } - if (amount == 0) { + if (number == 0) { throw new NotAvailableException("ColorState"); } // finally compute color average in rgb space - averageRed = averageRed / amount; - averageGreen = averageGreen / amount; - averageBlue = averageBlue / amount; + averageRed = averageRed / number; + averageGreen = averageGreen / number; + averageBlue = averageBlue / number; Builder serviceStateBuilder = ColorState.newBuilder(); @@ -156,7 +155,7 @@ public ColorState getColorState(final UnitType unitType) throws NotAvailableExce @Override public Future setNeutralWhite() { List> futureList = new ArrayList<>(); - for(ColorStateOperationService colorStateOperationService : getServices()) { + for (ColorStateOperationService colorStateOperationService : getServices()) { futureList.add(colorStateOperationService.setNeutralWhite()); } return FutureProcessor.allOf(ActionDescription.getDefaultInstance(), futureList); @@ -165,7 +164,7 @@ public Future setNeutralWhite() { @Override public Future setNeutralWhite(final ActionParameter actionParameter) { List> futureList = new ArrayList<>(); - for(ColorStateOperationService colorStateOperationService : getServices()) { + for (ColorStateOperationService colorStateOperationService : getServices()) { futureList.add(colorStateOperationService.setNeutralWhite(actionParameter)); } return FutureProcessor.allOf(ActionDescription.getDefaultInstance(), futureList); diff --git a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/AbstractUnitRemote.java b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/AbstractUnitRemote.java index f4c42b5d63..e0801800b8 100644 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/AbstractUnitRemote.java +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/AbstractUnitRemote.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -69,6 +69,7 @@ import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate; import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType; import org.slf4j.LoggerFactory; + import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -112,7 +113,7 @@ public void update(final DataProvider source, UnitRegistryData } } catch (NotAvailableException ex) { // unit config has been removed, probably because of deletion, Units will shutdown this remote - logger.debug("Could not update unit remote", ex); + logger.trace("Skip remote config update since unit is not yet or not anymore included in the registry."); } catch (CouldNotPerformException ex) { ExceptionPrinter.printHistory("Could not update unit config of " + this, ex, logger); } @@ -719,11 +720,13 @@ public Future queryRecord(final QueryType /** * {@inheritDoc} + * * @param actionId {@inheritDoc} * * @return {@inheritDoc} + * * @throws NotAvailableException {@inheritDoc} - * @throws InterruptedException {@inheritDoc} + * @throws InterruptedException {@inheritDoc} */ @Override public ActionDescription resolveRelatedActionDescription(String actionId) throws NotAvailableException, InterruptedException { @@ -746,7 +749,7 @@ public ActionDescription resolveRelatedActionDescription(String actionId) throws for (final ActionReference actionReference : actionDescription.getActionImpactList()) { // once the action is only precomputed we need to lookup it from its related unit. - if(actionReference.getActionId().equals(Action.PRECOMPUTED_ACTION_ID)) { + if (actionReference.getActionId().equals(Action.PRECOMPUTED_ACTION_ID)) { try { return Units.getUnit(actionReference.getServiceStateDescription().getUnitId(), true).resolveRelatedActionDescription(actionId); } catch (NotAvailableException ex) { @@ -763,6 +766,6 @@ public ActionDescription resolveRelatedActionDescription(String actionId) throws throw new NotAvailableException("RelatedAction", ex); } // no relation found. - throw new NotAvailableException("RelatedAction of ["+actionId+"]"); + throw new NotAvailableException("RelatedAction of [" + actionId + "]"); } } diff --git a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPool.kt b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPool.kt index 209264df21..2aaa885cce 100644 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPool.kt +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPool.kt @@ -45,12 +45,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock * #L% */ -class CustomUnitPool : Manageable>> { +class CustomUnitPool> : Manageable>> { private val UNIT_REMOTE_REGISTRY_LOCK = ReentrantReadWriteLock() private var unitRegistryDataObserver: Observer, UnitRegistryDataType.UnitRegistryData> - private var unitDataObserver: Observer, Message> + private var unitDataObserver: Observer, M> private var serviceStateObserver: Observer, out Message> - private var unitRemoteRegistry: RemoteControllerRegistry> + private var unitRemoteRegistry: RemoteControllerRegistry private var unitConfigDiff: ProtobufListDiff private var filterSet: MutableSet> private var unitDataObservable: ObservableImpl, Message> @@ -184,7 +184,7 @@ class CustomUnitPool : Manageable>> FatalImplementationErrorException("unid remote registered but pool was never activated!", this) } val unitRemote: UnitRemote = Units.getUnit(unitId, false) - unitRemoteRegistry.register(unitRemote) + unitRemoteRegistry.register(unitRemote as U) for (serviceType in unitRemote.availableServiceTypes) { unitRemote.addServiceStateObserver(serviceType, serviceStateObserver) } @@ -298,7 +298,7 @@ class CustomUnitPool : Manageable>> override fun isActive(): Boolean = active - val internalUnitList: List> + val internalUnitList: List get() = unitRemoteRegistry.entries companion object { diff --git a/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.java b/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.java deleted file mode 100644 index b90e840077..0000000000 --- a/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.openbase.bco.dal.remote.layer.unit; - -/*- - * #%L - * BCO DAL Test - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import static org.junit.jupiter.api.Assertions.*; -import com.google.protobuf.Message; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.openbase.bco.dal.lib.layer.unit.UnitRemote; -import org.openbase.bco.dal.test.AbstractBCODeviceManagerTest; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType; - -import java.util.List; -import java.util.concurrent.TimeUnit; - - - -public class CustomUnitPoolTest extends AbstractBCODeviceManagerTest { - - /** - * Test of getBatteryLevel method, of class BatteryRemote. - * - * @throws java.lang.Exception - */ - @Test - @Timeout(10) - public void testUnitPool() throws Exception { - final CustomUnitPool customUnitPool = new CustomUnitPool(); - assertEquals(false, customUnitPool.isActive(), "pool is active while never activated"); - - customUnitPool.activate(); - - customUnitPool.init( - unitConfig -> unitConfig.hasId(), - unitConfig -> unitConfig.getUnitType() == UnitType.BUTTON); - - customUnitPool.activate(); - - for (UnitRemote unitRemote : customUnitPool.getInternalUnitList()) { - assertEquals(UnitType.BUTTON, unitRemote.getUnitType(), "pool contains actually filtered entry!"); - System.out.println("is button: "+ unitRemote.getLabel()); - } - - final List buttonUnitConfig = Registries.getUnitRegistry().getUnitConfigsByUnitType(UnitType.BUTTON); - Registries.getUnitRegistry().updateUnitConfig(buttonUnitConfig.get(0).toBuilder().addAlias("MyButtonTestUnit").build()).get(5, TimeUnit.SECONDS); - - final List lightUnitConfig = Registries.getUnitRegistry().getUnitConfigsByUnitType(UnitType.COLORABLE_LIGHT); - Registries.getUnitRegistry().updateUnitConfig(lightUnitConfig.get(0).toBuilder().addAlias("MyLightestUnit").build()).get(5, TimeUnit.SECONDS); - - for (UnitRemote unitRemote : customUnitPool.getInternalUnitList()) { - assertEquals(UnitType.BUTTON, unitRemote.getUnitType(), "pool contains actually filtered entry!"); - System.out.println("is button: "+ unitRemote.getLabel()); - } - - customUnitPool.shutdown(); - } -} diff --git a/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.kt b/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.kt new file mode 100644 index 0000000000..c9ec97278e --- /dev/null +++ b/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.kt @@ -0,0 +1,78 @@ +package org.openbase.bco.dal.remote.layer.unit + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Timeout +import org.openbase.bco.dal.lib.layer.unit.UnitRemote +import org.openbase.bco.dal.test.AbstractBCODeviceManagerTest +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.pattern.Filter +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig +import org.openbase.type.domotic.unit.UnitTemplateType +import java.util.concurrent.TimeUnit + +/*- +* #%L +* BCO DAL Test +* %% +* Copyright (C) 2014 - 2021 openbase.org +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +class CustomUnitPoolTest : AbstractBCODeviceManagerTest() { + /** + * Test of getBatteryLevel method, of class BatteryRemote. + * + * @throws java.lang.Exception + */ + @Test + @Timeout(10) + @Throws(Exception::class) + fun testUnitPool() { + val customUnitPool: CustomUnitPool<*, *> = CustomUnitPool>() + Assertions.assertEquals(false, customUnitPool.isActive(), "pool is active while never activated") + customUnitPool.activate() + customUnitPool.init( + Filter { unitConfig: UnitConfig -> unitConfig.hasId() }, + Filter { unitConfig: UnitConfig -> unitConfig.unitType === UnitTemplateType.UnitTemplate.UnitType.BUTTON }) + customUnitPool.activate() + for (unitRemote in customUnitPool.internalUnitList) { + Assertions.assertEquals( + UnitTemplateType.UnitTemplate.UnitType.BUTTON, + unitRemote.getUnitType(), + "pool contains actually filtered entry!" + ) + println("is button: " + unitRemote.getLabel()) + } + val buttonUnitConfig = + Registries.getUnitRegistry().getUnitConfigsByUnitType(UnitTemplateType.UnitTemplate.UnitType.BUTTON) + Registries.getUnitRegistry() + .updateUnitConfig(buttonUnitConfig[0].toBuilder().addAlias("MyButtonTestUnit").build())[5, TimeUnit.SECONDS] + val lightUnitConfig = Registries.getUnitRegistry() + .getUnitConfigsByUnitType(UnitTemplateType.UnitTemplate.UnitType.COLORABLE_LIGHT) + Registries.getUnitRegistry() + .updateUnitConfig(lightUnitConfig[0].toBuilder().addAlias("MyLightestUnit").build())[5, TimeUnit.SECONDS] + for (unitRemote in customUnitPool.internalUnitList) { + Assertions.assertEquals( + UnitTemplateType.UnitTemplate.UnitType.BUTTON, + unitRemote.getUnitType(), + "pool contains actually filtered entry!" + ) + println("is button: " + unitRemote.getLabel()) + } + customUnitPool.shutdown() + } +} diff --git a/module/dal/test/src/test/java/org/openbase/bco/dal/test/action/KotlinTest.kt b/module/dal/test/src/test/java/org/openbase/bco/dal/test/action/KotlinTest.kt deleted file mode 100644 index 715a56e404..0000000000 --- a/module/dal/test/src/test/java/org/openbase/bco/dal/test/action/KotlinTest.kt +++ /dev/null @@ -1,4 +0,0 @@ -package org.openbase.bco.dal.test.action - -class KotlinTest { -} diff --git a/module/dal/visual/build.gradle.kts b/module/dal/visual/build.gradle.kts index 464c0ba09b..27d69a460b 100644 --- a/module/dal/visual/build.gradle.kts +++ b/module/dal/visual/build.gradle.kts @@ -1,5 +1,17 @@ plugins { id("org.openbase.bco") + id("org.openjfx.javafxplugin") version "0.1.0" +} + +javafx { + version = "21.0.1" + modules = listOf( + "javafx.base", + "javafx.graphics", + "javafx.media", + "javafx.controls", + "javafx.fxml" + ) } dependencies { diff --git a/module/dal/visual/src/main/java/org/openbase/bco/dal/visual/service/AbstractServicePanel.java b/module/dal/visual/src/main/java/org/openbase/bco/dal/visual/service/AbstractServicePanel.java index 068bc3594f..84da99743e 100644 --- a/module/dal/visual/src/main/java/org/openbase/bco/dal/visual/service/AbstractServicePanel.java +++ b/module/dal/visual/src/main/java/org/openbase/bco/dal/visual/service/AbstractServicePanel.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -27,26 +27,28 @@ import org.openbase.bco.dal.lib.layer.service.provider.ProviderService; import org.openbase.bco.dal.lib.layer.unit.UnitRemote; import org.openbase.bco.dal.visual.util.StatusPanel; -import org.openbase.jul.exception.*; import org.openbase.jul.exception.InstantiationException; +import org.openbase.jul.exception.*; +import org.openbase.jul.extension.type.processing.ScopeProcessor; import org.openbase.jul.iface.Shutdownable; import org.openbase.jul.pattern.Observer; import org.openbase.jul.pattern.controller.Remote; -import org.openbase.type.domotic.state.ConnectionStateType.ConnectionState; import org.openbase.jul.schedule.SyncObject; +import org.openbase.type.domotic.service.ServiceDescriptionType; +import org.openbase.type.domotic.service.ServiceTemplateType; +import org.openbase.type.domotic.state.ConnectionStateType.ConnectionState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.openbase.type.domotic.service.ServiceTemplateType; import javax.swing.*; import java.awt.*; import java.util.concurrent.Future; -import org.openbase.type.domotic.service.ServiceDescriptionType; /** * @param * @param * @param + * * @author Divine Threepwood */ public abstract class AbstractServicePanel extends javax.swing.JPanel implements Shutdownable { @@ -150,7 +152,7 @@ public String getServiceName() { } return "---"; } - + protected UnitRemote getUnitRemote() { return this.unitRemote; } @@ -231,6 +233,7 @@ public void setServiceType(ServiceTemplateType.ServiceTemplate.ServiceType servi * Initializes this service panel with the given unit remote. * * @param unitRemote + * * @throws CouldNotPerformException * @throws InterruptedException */ @@ -254,6 +257,7 @@ public void initObserver() throws CouldNotPerformException, InterruptedException * Make sure the remote unit was initialized before and the service description is compatible with this unit. * * @param serviceDescription the new service description to bind to this unit remote. + * * @throws CouldNotPerformException is thrown if any error occurs during the binding process. * @throws InterruptedException */ @@ -264,7 +268,7 @@ public void bindServiceConfig(final ServiceDescriptionType.ServiceDescription se } setServiceConfig(serviceDescription); } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not bind ServiceConfig[" + serviceDescription.getServiceType() + "] on UnitRemote[" + unitRemote.getScope() + "]!", ex); + throw new CouldNotPerformException("Could not bind ServiceConfig[" + serviceDescription.getServiceType() + "] on UnitRemote[" + ScopeProcessor.generateStringRep(unitRemote.getScope()) + "]!", ex); } } diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.java index 0b07dfc370..f579407fe0 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -24,7 +24,6 @@ import org.openbase.jps.core.AbstractJavaProperty; -import java.io.File; import java.net.URI; import java.util.List; @@ -36,7 +35,7 @@ public class JPOpenHABURI extends AbstractJavaProperty { private static final String[] ARGUMENT_IDENTIFIERS = {"URI"}; private static final String[] COMMAND_IDENTIFIERS = {"--openhab-url", "--openhab-uri", "--uri"}; - private static final String DEFAULT_URI = "http://localhost:8080"; + private static final String DEFAULT_URI = "http://openhab:8080"; public static final String SYSTEM_VARIABLE_OPENHAB_PORT = "OPENHAB_HTTP_PORT"; @@ -56,7 +55,7 @@ protected URI getPropertyDefaultValue() { // use system variable if defined String systemDefinedPort = System.getenv(SYSTEM_VARIABLE_OPENHAB_PORT); if (systemDefinedPort != null) { - return URI.create("http://localhost:"+systemDefinedPort); + return URI.create("http://localhost:" + systemDefinedPort); } return URI.create(DEFAULT_URI); diff --git a/module/registry/activity-registry/remote/src/main/java/org/openbase/bco/registry/activity/remote/CachedActivityRegistryRemote.java b/module/registry/activity-registry/remote/src/main/java/org/openbase/bco/registry/activity/remote/CachedActivityRegistryRemote.java index d8a4505862..ef320c974a 100644 --- a/module/registry/activity-registry/remote/src/main/java/org/openbase/bco/registry/activity/remote/CachedActivityRegistryRemote.java +++ b/module/registry/activity-registry/remote/src/main/java/org/openbase/bco/registry/activity/remote/CachedActivityRegistryRemote.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -44,7 +44,7 @@ public class CachedActivityRegistryRemote { private static final Logger LOGGER = LoggerFactory.getLogger(CachedActivityRegistryRemote.class); private static final SyncObject REMOTE_LOCK = new SyncObject("CachedActivityRegistryRemoteLock"); - private static final SyncObject REGISTY_LOCK = new SyncObject("RegistyLock"); + private static final SyncObject REGISTRY_LOCK = new SyncObject("RegistryLock"); private static ActivityRegistryRemote registryRemote; private static volatile boolean shutdown = false; @@ -75,7 +75,7 @@ public static void reinitialize() throws InterruptedException, CouldNotPerformEx try { // only call re-init if the registry was activated and initialized in the first place if (registryRemote != null) { - synchronized (REGISTY_LOCK) { + synchronized (REGISTRY_LOCK) { getRegistry().reinit(REMOTE_LOCK); } } @@ -119,7 +119,7 @@ public static ActivityRegistryRemote getRegistry() throws NotAvailableException return registryRemote; } - synchronized (REGISTY_LOCK) { + synchronized (REGISTRY_LOCK) { if (registryRemote == null) { try { registryRemote = new ActivityRegistryRemote(); diff --git a/module/registry/class-registry/remote/src/main/java/org/openbase/bco/registry/clazz/remote/CachedClassRegistryRemote.java b/module/registry/class-registry/remote/src/main/java/org/openbase/bco/registry/clazz/remote/CachedClassRegistryRemote.java index f9edead19a..27328025c9 100644 --- a/module/registry/class-registry/remote/src/main/java/org/openbase/bco/registry/clazz/remote/CachedClassRegistryRemote.java +++ b/module/registry/class-registry/remote/src/main/java/org/openbase/bco/registry/clazz/remote/CachedClassRegistryRemote.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -44,7 +44,7 @@ public class CachedClassRegistryRemote { private static final Logger LOGGER = LoggerFactory.getLogger(CachedClassRegistryRemote.class); private static final SyncObject REMOTE_LOCK = new SyncObject("CachedClassRegistryRemoteLock"); - private static final SyncObject REGISTY_LOCK = new SyncObject("RegistyLock"); + private static final SyncObject REGISTRY_LOCK = new SyncObject("RegistryLock"); private static ClassRegistryRemote registryRemote; private static volatile boolean shutdown = false; @@ -75,7 +75,7 @@ public static void reinitialize() throws InterruptedException, CouldNotPerformEx try { // only call re-init if the registry was activated and initialized in the first place if (registryRemote != null) { - synchronized (REGISTY_LOCK) { + synchronized (REGISTRY_LOCK) { getRegistry().reinit(REMOTE_LOCK); } } @@ -119,7 +119,7 @@ public static ClassRegistryRemote getRegistry() throws NotAvailableException { return registryRemote; } - synchronized (REGISTY_LOCK) { + synchronized (REGISTRY_LOCK) { if (registryRemote == null) { try { registryRemote = new ClassRegistryRemote(); diff --git a/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/com/AbstractRegistryController.java b/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/com/AbstractRegistryController.java index ecb4f1efc5..5a804588d8 100644 --- a/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/com/AbstractRegistryController.java +++ b/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/com/AbstractRegistryController.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -190,7 +190,7 @@ public void activate() throws InterruptedException, CouldNotPerformException { performInitialConsistencyCheck(); } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not activate "+this+" registry!", ex); + throw new CouldNotPerformException("Could not activate " + this + " registry!", ex); } } diff --git a/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/util/LocationUtils.java b/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/util/LocationUtils.java deleted file mode 100644 index 0f71b830cf..0000000000 --- a/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/util/LocationUtils.java +++ /dev/null @@ -1,233 +0,0 @@ -package org.openbase.bco.registry.lib.util; - -/* - * #%L - * BCO Registry Lib - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ -import java.util.*; - -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.InvalidStateException; -import org.openbase.jul.exception.NotAvailableException; -import org.openbase.jul.extension.protobuf.IdentifiableMessage; -import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap; -import org.openbase.jul.storage.registry.ConsistencyHandler; -import org.openbase.jul.storage.registry.EntryModification; -import org.openbase.jul.storage.registry.ProtoBufRegistry; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.location.LocationConfigType.LocationConfig.LocationType; -import org.openbase.type.spatial.PlacementConfigType; - -/** - * - * @author Divine Threepwood - * - */ -public class LocationUtils { - - public static void validateRootLocation(final UnitConfig newRootLocation, final ProtoBufMessageMap entryMap, final ConsistencyHandler consistencyHandler) throws CouldNotPerformException, EntryModification { - try { - boolean modified = false; - // detect root location - IdentifiableMessage detectedRootLocationConfigEntry = entryMap.get(newRootLocation.getId()); - UnitConfig.Builder detectedRootLocationConfigBuilder = detectedRootLocationConfigEntry.getMessage().toBuilder(); - - // verify if root flag is set. - if (!detectedRootLocationConfigBuilder.getLocationConfig().hasRoot() || !detectedRootLocationConfigBuilder.getLocationConfig().getRoot()) { - detectedRootLocationConfigBuilder.getLocationConfigBuilder().setRoot(true); - modified = true; - } - - // verify if placement field is set. - if (!detectedRootLocationConfigBuilder.hasPlacementConfig()) { - detectedRootLocationConfigBuilder.setPlacementConfig(PlacementConfigType.PlacementConfig.newBuilder()); - modified = true; - } - - // verify if placement location id is set. - if (!detectedRootLocationConfigBuilder.getPlacementConfig().hasLocationId() || !detectedRootLocationConfigBuilder.getPlacementConfig().getLocationId().equals(detectedRootLocationConfigBuilder.getId())) { - detectedRootLocationConfigBuilder.getPlacementConfigBuilder().setLocationId(detectedRootLocationConfigBuilder.getId()); - modified = true; - } - - if (modified) { - throw new EntryModification(detectedRootLocationConfigEntry.setMessage(detectedRootLocationConfigBuilder, consistencyHandler), consistencyHandler); - } - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not validate root location!", ex); - } - } - - public static UnitConfig getRootLocation(final ProtoBufMessageMap entryMap) throws CouldNotPerformException { - return getRootLocation(entryMap.getMessages()); - } - - public static UnitConfig getRootLocation(final List locationUnitConfigList) throws CouldNotPerformException { - UnitConfig rootLocation = null; - try { - for (UnitConfig locationConfig : locationUnitConfigList) { - if (locationConfig.getLocationConfig().hasRoot() && locationConfig.getLocationConfig().getRoot()) { - if (rootLocation != null) { - throw new InvalidStateException("Found more than one [" + rootLocation.getLabel() + "] & [" + locationConfig.getLabel() + "] root locations!"); - } - rootLocation = locationConfig; - } - } - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not lookup root location!", ex); - } - - if (rootLocation == null) { - throw new NotAvailableException("root location"); - } - return rootLocation; - } - - public static UnitConfig detectRootLocation(final UnitConfig currentLocationConfig, final ProtoBufMessageMap entryMap, final ConsistencyHandler consistencyHandler) throws CouldNotPerformException, EntryModification { - try { - try { - return getRootLocation(entryMap); - } catch (NotAvailableException ex) { - UnitConfig newLocationConfig = computeNewRootLocation(currentLocationConfig, entryMap); - validateRootLocation(newLocationConfig, entryMap, consistencyHandler); - return newLocationConfig; - } - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not detect root location!", ex); - } - } - - public static UnitConfig computeNewRootLocation(final UnitConfig currentLocationConfig, ProtoBufMessageMap entryMap) throws CouldNotPerformException { - try { - HashMap rootLocationConfigList = new HashMap<>(); - for (UnitConfig locationConfig : entryMap.getMessages()) { - rootLocationConfigList.put(locationConfig.getId(), locationConfig); - } - - rootLocationConfigList.put(currentLocationConfig.getId(), currentLocationConfig); - - if (rootLocationConfigList.size() == 1) { - for (UnitConfig unitConfig : rootLocationConfigList.values()) { - return Optional.of(unitConfig).get(); - } - return Optional.empty().get(); - } - - for (UnitConfig locationConfig : new ArrayList<>(rootLocationConfigList.values())) { - if (!locationConfig.hasPlacementConfig()) { - } else if (!locationConfig.getPlacementConfig().hasLocationId()) { - } else if (locationConfig.getPlacementConfig().getLocationId().isEmpty()) { - } else if (locationConfig.getPlacementConfig().getLocationId().equals(locationConfig.getId())) { - return locationConfig; - } else { - rootLocationConfigList.remove(locationConfig.getId()); - } - } - - if (rootLocationConfigList.isEmpty()) { - throw new NotAvailableException("root candidate"); - } else if (rootLocationConfigList.size() == 1) { - for (UnitConfig unitConfig : rootLocationConfigList.values()) { - return Optional.of(unitConfig).get(); - } - return Optional.empty().get(); - } - - throw new InvalidStateException("To many potential root locations detected!"); - - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not compute root location!", ex); - } - } - - /** - * Detect the type of a location. - * Since a location, except the root location, always has a parent the only ambiguous case - * is when the parent is a zone but no children are defined. Than the location can either be - * a zone or a tile. In this case an exception is thrown. - * - * @param locationUnit the location of which the type is detected - * @param locationRegistry the location registry - * @return the type locationUnit should have - * @throws CouldNotPerformException if the type is ambiguous - */ - public static LocationType detectLocationType(UnitConfig locationUnit, ProtoBufRegistry locationRegistry) throws CouldNotPerformException { - try { - if (!locationUnit.hasPlacementConfig()) { - throw new NotAvailableException("placementConfig"); - } - if (!locationUnit.getPlacementConfig().hasLocationId() || locationUnit.getPlacementConfig().getLocationId().isEmpty()) { - throw new NotAvailableException("placementConfig.locationId"); - } - - if (locationUnit.getLocationConfig().getRoot()) { - return LocationType.ZONE; - } - - LocationType parentLocationType = locationRegistry.get(locationUnit.getPlacementConfig().getLocationId()).getMessage().getLocationConfig().getLocationType(); - Set childLocationTypes = new HashSet<>(); - for (String childId : locationUnit.getLocationConfig().getChildIdList()) { - childLocationTypes.add(locationRegistry.get(childId).getMessage().getLocationConfig().getLocationType()); - } - - return detectLocationType(parentLocationType, childLocationTypes); - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not detect Type for location [" + locationUnit.getLabel() + "]", ex); - } - } - - /** - * Detect the type of a location. - * Since a location, except the root location, always has a parent the only ambiguous case - * is when the parent is a zone but no children are defined. Than the location can either be - * a zone or a tile. In this case an exception is thrown. - * - * @param parentLocationType the type of the parent location of the location whose type is detected - * @param childLocationTypes a set of all immediate child types of the location whose type is detected - * @return the type locationUnit should have - * @throws CouldNotPerformException if the type is ambiguous - */ - private static LocationType detectLocationType(LocationType parentLocationType, Set childLocationTypes) throws CouldNotPerformException { - // if the parent is a region or tile than the location has to be a region - if (parentLocationType == LocationType.REGION || parentLocationType == LocationType.TILE) { - return LocationType.REGION; - } - - // if one child is a zone or a tile the location has to be a zone - if (childLocationTypes.contains(LocationType.ZONE) || childLocationTypes.contains(LocationType.TILE)) { - return LocationType.ZONE; - } - - // if the parent is a zone and a child is a region than the location has to be a tile - if (parentLocationType == LocationType.ZONE && childLocationTypes.contains(LocationType.REGION)) { - return LocationType.TILE; - } - - // if the parent type is a zone but no childs are defined the location could be a tile or a zone which leaves the type undefined - String childTypes = ""; - String acc = childTypes; - for (LocationType locationType : childLocationTypes) { - String s = locationType.toString() + " "; - acc = acc.concat(s); - } - childTypes = "[ " + acc + "]"; - throw new CouldNotPerformException("Could not detect locationType from parentType[" + parentLocationType.name() + "] and childTypes" + childTypes); - } -} diff --git a/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/util/LocationUtils.kt b/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/util/LocationUtils.kt new file mode 100644 index 0000000000..37050e2f4d --- /dev/null +++ b/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/util/LocationUtils.kt @@ -0,0 +1,250 @@ +package org.openbase.bco.registry.lib.util + +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.InvalidStateException +import org.openbase.jul.exception.NotAvailableException +import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap +import org.openbase.jul.storage.registry.ConsistencyHandler +import org.openbase.jul.storage.registry.EntryModification +import org.openbase.jul.storage.registry.ProtoBufRegistry +import org.openbase.type.domotic.unit.UnitConfigType +import org.openbase.type.domotic.unit.location.LocationConfigType.LocationConfig.LocationType +import org.openbase.type.spatial.PlacementConfigType +import java.util.* + +/** + * + * @author [Divine Threepwood](mailto:divine@openbase.org) + */ +object LocationUtils { + @JvmStatic + @Throws(CouldNotPerformException::class, EntryModification::class) + fun validateRootLocation( + newRootLocation: UnitConfigType.UnitConfig, + entryMap: ProtoBufMessageMap, + consistencyHandler: ConsistencyHandler<*, *, *, *>, + ) { + try { + var modified = false + // detect root location + val detectedRootLocationConfigEntry = entryMap[newRootLocation.id] + val detectedRootLocationConfigBuilder = detectedRootLocationConfigEntry.message.toBuilder() + + // verify if root flag is set. + if (!detectedRootLocationConfigBuilder.locationConfig.hasRoot() || !detectedRootLocationConfigBuilder.locationConfig.root) { + detectedRootLocationConfigBuilder.locationConfigBuilder.setRoot(true) + modified = true + } + + // verify if placement field is set. + if (!detectedRootLocationConfigBuilder.hasPlacementConfig()) { + detectedRootLocationConfigBuilder.setPlacementConfig(PlacementConfigType.PlacementConfig.newBuilder()) + modified = true + } + + // verify if placement location id is set. + if (!detectedRootLocationConfigBuilder.placementConfig.hasLocationId() || detectedRootLocationConfigBuilder.placementConfig.locationId != detectedRootLocationConfigBuilder.id) { + detectedRootLocationConfigBuilder.placementConfigBuilder.setLocationId(detectedRootLocationConfigBuilder.id) + modified = true + } + + if (modified) { + throw EntryModification( + detectedRootLocationConfigEntry.setMessage( + detectedRootLocationConfigBuilder, + consistencyHandler + ), consistencyHandler + ) + } + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException("Could not validate root location!", ex) + } + } + + @JvmStatic + @Throws(CouldNotPerformException::class) + fun getRootLocation(entryMap: ProtoBufMessageMap): UnitConfigType.UnitConfig { + return getRootLocation(entryMap.getMessages()) + } + + @JvmStatic + @Throws(CouldNotPerformException::class) + fun getRootLocation(locationUnitConfigList: List): UnitConfigType.UnitConfig { + var rootLocation: UnitConfigType.UnitConfig? = null + try { + for (locationConfig in locationUnitConfigList) { + if (locationConfig.locationConfig.hasRoot() && locationConfig.locationConfig.root) { + if (rootLocation != null) { + throw InvalidStateException("Found more than one [" + rootLocation.label + "] & [" + locationConfig.label + "] root locations!") + } + rootLocation = locationConfig + } + } + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException("Could not lookup root location!", ex) + } + + if (rootLocation == null) { + throw NotAvailableException("root location") + } + return rootLocation + } + + @JvmStatic + @Throws(CouldNotPerformException::class, EntryModification::class) + fun detectRootLocation( + currentLocationConfig: UnitConfigType.UnitConfig, + entryMap: ProtoBufMessageMap, + consistencyHandler: ConsistencyHandler<*, *, *, *>, + ): UnitConfigType.UnitConfig { + try { + try { + return getRootLocation(entryMap) + } catch (ex: NotAvailableException) { + val newLocationConfig = computeNewRootLocation(currentLocationConfig, entryMap) + validateRootLocation(newLocationConfig, entryMap, consistencyHandler) + return newLocationConfig + } + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException("Could not detect root location!", ex) + } + } + + @JvmStatic + @Throws(CouldNotPerformException::class) + fun computeNewRootLocation( + currentLocationConfig: UnitConfigType.UnitConfig, + entryMap: ProtoBufMessageMap, + ): UnitConfigType.UnitConfig { + try { + val rootLocationConfigList = HashMap() + for (locationConfig in entryMap.getMessages()) { + rootLocationConfigList[locationConfig.id] = locationConfig + } + + rootLocationConfigList[currentLocationConfig.id] = currentLocationConfig + + if (rootLocationConfigList.size == 1) { + for (unitConfig in rootLocationConfigList.values) { + return Optional.of(unitConfig).get() + } + return Optional.empty().get() + } + + for (locationConfig in ArrayList(rootLocationConfigList.values)) { + if (!locationConfig.hasPlacementConfig()) { + } else if (!locationConfig.placementConfig.hasLocationId()) { + } else if (locationConfig.placementConfig.locationId.isEmpty()) { + } else if (locationConfig.placementConfig.locationId == locationConfig.id) { + return locationConfig + } else { + rootLocationConfigList.remove(locationConfig.id) + } + } + + if (rootLocationConfigList.isEmpty()) { + throw NotAvailableException("root candidate") + } else if (rootLocationConfigList.size == 1) { + for (unitConfig in rootLocationConfigList.values) { + return Optional.of(unitConfig).get() + } + return Optional.empty().get() + } + + throw InvalidStateException("Too many potential root locations detected!") + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException("Could not compute root location!", ex) + } + } + + /** + * Detect the type of a location. + * Since a location, except the root location, always has a parent the only ambiguous case + * is when the parent is a zone but no children are defined. Than the location can either be + * a zone or a tile. In this case an exception is thrown. + * + * @param locationUnit the location of which the type is detected + * @param locationRegistry the location registry + * @return the type locationUnit should have + * @throws CouldNotPerformException if the type is ambiguous + */ + @JvmStatic + @Throws(CouldNotPerformException::class) + fun detectLocationType( + locationUnit: UnitConfigType.UnitConfig, + locationRegistry: ProtoBufRegistry, + ): LocationType { + try { + if (!locationUnit.hasPlacementConfig()) { + throw NotAvailableException("placementConfig") + } + if (!locationUnit.placementConfig.hasLocationId() || locationUnit.placementConfig.locationId.isEmpty()) { + throw NotAvailableException("placementConfig.locationId") + } + + if (locationUnit.locationConfig.root) { + return LocationType.ZONE + } + + val parentLocationType = + locationRegistry[locationUnit.placementConfig.locationId].message.locationConfig.locationType + + val childLocationTypes: List = locationUnit.locationConfig.childIdList + .map { childId -> locationRegistry[childId].message.locationConfig.locationType } + .distinct() + .filter { it != LocationType.UNKNOWN } + + return detectLocationType(parentLocationType, childLocationTypes) + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException("Could not detect Type for location [" + locationUnit.label + "]", ex) + } + } + + /** + * Detect the type of a location. + * Since a location, except the root location, always has a parent the only ambiguous case + * is when the parent is a zone but no children are defined. Then the location can either be + * a zone or a tile. In this case an exception is thrown. + * + * @param parentLocationType the type of the parent location of the location whose type is detected + * @param childLocationTypes a set of all immediate child types of the location whose type is detected + * @return the type locationUnit should have + * @throws CouldNotPerformException if the type is ambiguous + */ + @JvmStatic + @Throws(CouldNotPerformException::class) + private fun detectLocationType( + parentLocationType: LocationType, + childLocationTypes: List, + ): LocationType { + + when (parentLocationType) { + // if the parent is a region or tile then the location has to be a region + LocationType.REGION, LocationType.TILE -> { + return LocationType.REGION + } + + LocationType.ZONE -> { + + // if the parent is a zone and has no children then it has to be + // a tile since each branch could contain exactly one tile + if (childLocationTypes.isEmpty()) { + return LocationType.TILE + } + + // if one child is a zone or a tile the location has to be a zone + if (childLocationTypes.contains(LocationType.ZONE) || childLocationTypes.contains(LocationType.TILE)) { + return LocationType.ZONE + } + + // if the parent is a zone and a child is a region than the location has to be a tile + if (childLocationTypes.contains(LocationType.REGION)) { + return LocationType.TILE + } + } + + LocationType.UNKNOWN -> {} // skip detection + } + throw CouldNotPerformException("Could not detect locationType from parentType[${parentLocationType.name}] and childTypes ${childLocationTypes.map { it.name }}") + } +} diff --git a/module/registry/message-registry/core/src/main/java/org/openbase/bco/registry/message/core/MessageRegistryController.java b/module/registry/message-registry/core/src/main/java/org/openbase/bco/registry/message/core/MessageRegistryController.java index da7a5366f5..14e599937b 100644 --- a/module/registry/message-registry/core/src/main/java/org/openbase/bco/registry/message/core/MessageRegistryController.java +++ b/module/registry/message-registry/core/src/main/java/org/openbase/bco/registry/message/core/MessageRegistryController.java @@ -10,24 +10,21 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . * #L% */ -import com.google.protobuf.Descriptors.FieldDescriptor; import org.openbase.bco.authentication.lib.AuthenticatedServiceProcessor; -import org.openbase.bco.authentication.lib.AuthorizationHelper; import org.openbase.bco.authentication.lib.AuthorizationHelper.PermissionType; import org.openbase.bco.registry.lib.com.AbstractRegistryController; -import org.openbase.bco.registry.lib.jp.JPBCODatabaseDirectory; import org.openbase.bco.registry.message.lib.MessageRegistry; import org.openbase.bco.registry.message.lib.generator.UserMessageIdGenerator; import org.openbase.bco.registry.message.lib.jp.JPMessageRegistryScope; @@ -40,16 +37,13 @@ import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.NotAvailableException; -import org.openbase.jul.pattern.ListFilter; import org.openbase.jul.schedule.GlobalCachedExecutorService; import org.openbase.jul.storage.registry.ProtoBufFileSynchronizedRegistry; import org.openbase.type.domotic.authentication.AuthenticatedValueType.AuthenticatedValue; -import org.openbase.type.domotic.authentication.UserClientPairType.UserClientPair; import org.openbase.type.domotic.communication.UserMessageType.UserMessage; import org.openbase.type.domotic.registry.MessageRegistryDataType.MessageRegistryData; import org.slf4j.LoggerFactory; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.Future; @@ -131,10 +125,7 @@ public void notifyChange() throws CouldNotPerformException, InterruptedException @Override public Future registerUserMessage(final UserMessage userMessage) { - return GlobalCachedExecutorService.submit(() -> { - UserMessage result = userMessageRegistry.register(userMessage); - return result; - }); + return GlobalCachedExecutorService.submit(() -> userMessageRegistry.register(userMessage)); } @Override @@ -226,8 +217,14 @@ public Future removeUserMessage(final UserMessage userMessage) { public Future removeUserMessageAuthenticated(final AuthenticatedValue authenticatedValue) { return GlobalCachedExecutorService.submit(() -> AuthenticatedServiceProcessor.authenticatedAction(authenticatedValue, UserMessage.class, this, (userMessage, authenticationBaseData) -> { - // verify write permissions for the old user message - final UserMessage old = userMessageRegistry.getMessage(userMessage.getId()); + // verify write permissions for the user message to remove + UserMessage old = null; + try { + old = userMessageRegistry.getMessage(userMessage.getId()); + } catch (NotAvailableException ex) { + // skip removal if message is not present. + return userMessage; + } AuthorizationWithTokenHelper.canDo(authenticationBaseData, old, PermissionType.WRITE, CachedUnitRegistryRemote.getRegistry()); return userMessageRegistry.remove(userMessage); } @@ -252,35 +249,35 @@ public Boolean isUserMessageRegistryConsistent() { protected void registerRemoteRegistries() { } - @Override - protected MessageRegistryData filterDataForUser(final MessageRegistryData.Builder dataBuilder, final UserClientPair userClientPair) throws CouldNotPerformException { - // Create a filter which removes all user messages from a list without read permissions to its location by the user - final ListFilter readFilter = userMessage -> { - try { - boolean senderPermission = AuthorizationHelper.canRead(CachedUnitRegistryRemote.getRegistry().getUnitConfigById(userMessage.getSenderId()), userClientPair, CachedUnitRegistryRemote.getRegistry().getAuthorizationGroupUnitConfigRemoteRegistry(true).getEntryMap(), CachedUnitRegistryRemote.getRegistry().getLocationUnitConfigRemoteRegistry(true).getEntryMap()); - boolean receiverPermission = AuthorizationHelper.canRead(CachedUnitRegistryRemote.getRegistry().getUnitConfigById(userMessage.getRecipientId()), userClientPair, CachedUnitRegistryRemote.getRegistry().getAuthorizationGroupUnitConfigRemoteRegistry(true).getEntryMap(), CachedUnitRegistryRemote.getRegistry().getLocationUnitConfigRemoteRegistry(true).getEntryMap()); - return !(senderPermission || receiverPermission); - } catch (CouldNotPerformException e) { - // if id could not resolved, than we filter the element. - return true; - } - }; - // iterate over all fields of unit registry data - for (FieldDescriptor fieldDescriptor : dataBuilder.getAllFields().keySet()) { - // only filter repeated fields - if (!fieldDescriptor.isRepeated()) { - continue; - } - - // only filter fields of type UserMessage - if (!fieldDescriptor.getMessageType().getName().equals(UserMessage.getDescriptor().getName())) { - continue; - } - - // copy list, filter it and set as new list for the field - dataBuilder.setField(fieldDescriptor, readFilter.filter(new ArrayList<>((List) dataBuilder.getField(fieldDescriptor)))); - } - - return dataBuilder.build(); - } +// @Override +// protected MessageRegistryData filterDataForUser(final MessageRegistryData.Builder dataBuilder, final UserClientPair userClientPair) throws CouldNotPerformException { +// // Create a filter which removes all user messages from a list without read permissions to its location by the user +// final ListFilter readFilter = userMessage -> { +// try { +// boolean senderPermission = AuthorizationHelper.canRead(CachedUnitRegistryRemote.getRegistry().getUnitConfigById(userMessage.getSenderId()), userClientPair, CachedUnitRegistryRemote.getRegistry().getAuthorizationGroupUnitConfigRemoteRegistry(true).getEntryMap(), CachedUnitRegistryRemote.getRegistry().getLocationUnitConfigRemoteRegistry(true).getEntryMap()); +// boolean receiverPermission = AuthorizationHelper.canRead(CachedUnitRegistryRemote.getRegistry().getUnitConfigById(userMessage.getRecipientId()), userClientPair, CachedUnitRegistryRemote.getRegistry().getAuthorizationGroupUnitConfigRemoteRegistry(true).getEntryMap(), CachedUnitRegistryRemote.getRegistry().getLocationUnitConfigRemoteRegistry(true).getEntryMap()); +// return !(senderPermission || receiverPermission); +// } catch (CouldNotPerformException e) { +// // if id could not resolved, than we filter the element. +// return true; +// } +// }; +// // iterate over all fields of unit registry data +// for (FieldDescriptor fieldDescriptor : dataBuilder.getAllFields().keySet()) { +// // only filter repeated fields +// if (!fieldDescriptor.isRepeated()) { +// continue; +// } +// +// // only filter fields of type UserMessage +// if (!fieldDescriptor.getMessageType().getName().equals(UserMessage.getDescriptor().getName())) { +// continue; +// } +// +// // copy list, filter it and set as new list for the field +// dataBuilder.setField(fieldDescriptor, readFilter.filter(new ArrayList<>((List) dataBuilder.getField(fieldDescriptor)))); +// } +// +// return dataBuilder.build(); +// } } diff --git a/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/CachedMessageRegistryRemote.java b/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/CachedMessageRegistryRemote.java index 77ece7b378..1f7d7268d9 100644 --- a/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/CachedMessageRegistryRemote.java +++ b/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/CachedMessageRegistryRemote.java @@ -10,25 +10,23 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ -import org.openbase.bco.registry.clazz.remote.ClassRegistryRemote; import org.openbase.bco.registry.message.lib.MessageRegistry; import org.openbase.jps.core.JPService; import org.openbase.jul.exception.*; import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.iface.Shutdownable; -import org.openbase.jul.iface.Shutdownable.ShutdownDaemon; import org.openbase.jul.schedule.SyncObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +44,7 @@ public class CachedMessageRegistryRemote { private static final Logger LOGGER = LoggerFactory.getLogger(CachedMessageRegistryRemote.class); private static final SyncObject REMOTE_LOCK = new SyncObject("CachedMessageRegistryRemoteLock"); - private static final SyncObject REGISTY_LOCK = new SyncObject("RegistyLock"); + private static final SyncObject REGISTRY_LOCK = new SyncObject("RegistryLock"); private static MessageRegistryRemote registryRemote; private static volatile boolean shutdown = false; @@ -77,7 +75,7 @@ public static void reinitialize() throws InterruptedException, CouldNotPerformEx try { // only call re-init if the registry was activated and initialized in the first place if (registryRemote != null) { - if (registryRemote != null) { + synchronized (REGISTRY_LOCK) { getRegistry().reinit(REMOTE_LOCK); } } @@ -121,7 +119,7 @@ public static MessageRegistryRemote getRegistry() throws NotAvailableException { return registryRemote; } - synchronized (REGISTY_LOCK) { + synchronized (REGISTRY_LOCK) { if (registryRemote == null) { try { registryRemote = new MessageRegistryRemote(); @@ -134,13 +132,13 @@ public static MessageRegistryRemote getRegistry() throws NotAvailableException { registryRemote.shutdown(); registryRemote = null; } - throw ExceptionPrinter.printHistoryAndReturnThrowable(new CouldNotPerformException("Could not start cached unit registry remote!", ex), LOGGER); + throw ExceptionPrinter.printHistoryAndReturnThrowable(new CouldNotPerformException("Could not start cached message registry remote!", ex), LOGGER); } } return registryRemote; } } catch (CouldNotPerformException ex) { - throw new NotAvailableException("Cached unit registry is not available!", ex); + throw new NotAvailableException("cached message registry", ex); } } @@ -176,9 +174,8 @@ public static void waitUntilReady() throws InterruptedException, CouldNotPerform getRegistry().waitUntilReady(); } - public void prepare() throws CouldNotPerformException, InterruptedException { + public static void prepare() throws CouldNotPerformException { synchronized (REMOTE_LOCK) { - // handle legal operation if (registryRemote == null && shutdown == false) { getRegistry(); diff --git a/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemote.java b/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemote.java index cf469f312f..05c3ed056b 100644 --- a/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemote.java +++ b/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemote.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -36,13 +36,17 @@ import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.InvalidStateException; import org.openbase.jul.exception.NotAvailableException; +import org.openbase.jul.exception.printer.ExceptionPrinter; +import org.openbase.jul.extension.type.processing.MultiLanguageTextProcessor; import org.openbase.jul.extension.type.util.TransactionSynchronizationFuture; import org.openbase.jul.storage.registry.RegistryRemote; import org.openbase.type.domotic.authentication.AuthenticatedValueType.AuthenticatedValue; import org.openbase.type.domotic.communication.UserMessageType.UserMessage; import org.openbase.type.domotic.registry.MessageRegistryDataType.MessageRegistryData; +import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.concurrent.Future; /** @@ -88,6 +92,7 @@ public SynchronizedRemoteRegistry getU } return userMessageRemoteRegistry; } + /** * {@inheritDoc} * @@ -124,6 +129,27 @@ public UserMessage getUserMessageById(final String userMessageId) throws NotAvai } } + public List getUserMessagesByText(final String text, final Locale locale) { + try { + validateData(); + return userMessageRemoteRegistry + .getMessages().stream() + .filter(userMessage -> { + try { + return MultiLanguageTextProcessor + .getBestMatch(locale, userMessage.getText()) + .equals(text); + } catch (NotAvailableException ex) { + return false; + } + }) + .toList(); + } catch (CouldNotPerformException ex) { + ExceptionPrinter.printHistory(new NotAvailableException("UserMessages", ex), logger); + return Collections.emptyList(); + } + } + @Override public List getUserMessages() throws CouldNotPerformException { try { @@ -156,7 +182,7 @@ public Boolean containsUserMessageById(final String userMessageId) { @Override public Future updateUserMessage(final UserMessage userMessage) { - return AuthenticatedServiceProcessor.requestAuthenticatedAction(userMessage, UserMessage.class, SessionManager.getInstance(), authenticatedValue -> updateUserMessageAuthenticated(authenticatedValue)); + return AuthenticatedServiceProcessor.requestAuthenticatedAction(userMessage, UserMessage.class, SessionManager.getInstance(), this::updateUserMessageAuthenticated); } @Override @@ -166,7 +192,7 @@ public Future updateUserMessageAuthenticated(final Authentic @Override public Future removeUserMessage(final UserMessage userMessage) { - return AuthenticatedServiceProcessor.requestAuthenticatedAction(userMessage, UserMessage.class, SessionManager.getInstance(), authenticatedValue -> removeUserMessageAuthenticated(authenticatedValue)); + return AuthenticatedServiceProcessor.requestAuthenticatedAction(userMessage, UserMessage.class, SessionManager.getInstance(), this::removeUserMessageAuthenticated); } @Override diff --git a/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemoteAuthExtensions.kt b/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemoteAuthExtensions.kt new file mode 100644 index 0000000000..0b1b8639cf --- /dev/null +++ b/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemoteAuthExtensions.kt @@ -0,0 +1,39 @@ +package org.openbase.bco.registry.message.remote + +import org.openbase.bco.authentication.lib.SessionManager +import org.openbase.bco.authentication.lib.future.AuthenticatedValueFuture +import org.openbase.type.domotic.authentication.AuthTokenType +import org.openbase.type.domotic.authentication.AuthenticatedValueType +import org.openbase.type.domotic.communication.UserMessageType +import java.io.Serializable +import java.util.concurrent.Future + +fun MessageRegistryRemote.updateUserMessageAuthenticated( + userMessage: UserMessageType.UserMessage?, + auth: AuthTokenType.AuthToken?, +): Future = authRequest(userMessage, ::updateUserMessageAuthenticated, auth) + +fun MessageRegistryRemote.removeUserMessageAuthenticated( + userMessage: UserMessageType.UserMessage?, + auth: AuthTokenType.AuthToken?, +): Future = authRequest(userMessage, ::removeUserMessageAuthenticated, auth) + +fun MessageRegistryRemote.registerUserMessageAuthenticated( + userMessage: UserMessageType.UserMessage?, + auth: AuthTokenType.AuthToken?, +): Future = authRequest(userMessage, ::registerUserMessageAuthenticated, auth) + +inline fun MessageRegistryRemote.authRequest( + value: T?, + origin: (AuthenticatedValueType.AuthenticatedValue) -> Future, + auth: AuthTokenType.AuthToken?, +): Future = with(SessionManager.getInstance()) { + initializeRequest(value, auth).let { value -> + AuthenticatedValueFuture( + origin(value), + T::class.java, + value.ticketAuthenticatorWrapper, + this + ) + } +} diff --git a/module/registry/remote/src/main/java/org/openbase/bco/registry/remote/Registries.java b/module/registry/remote/src/main/java/org/openbase/bco/registry/remote/Registries.java index eaaf4a79ac..00fd75f212 100644 --- a/module/registry/remote/src/main/java/org/openbase/bco/registry/remote/Registries.java +++ b/module/registry/remote/src/main/java/org/openbase/bco/registry/remote/Registries.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -30,6 +30,9 @@ import org.openbase.bco.registry.clazz.lib.ClassRegistry; import org.openbase.bco.registry.clazz.remote.CachedClassRegistryRemote; import org.openbase.bco.registry.clazz.remote.ClassRegistryRemote; +import org.openbase.bco.registry.message.lib.MessageRegistry; +import org.openbase.bco.registry.message.remote.CachedMessageRegistryRemote; +import org.openbase.bco.registry.message.remote.MessageRegistryRemote; import org.openbase.bco.registry.template.lib.TemplateRegistry; import org.openbase.bco.registry.template.remote.CachedTemplateRegistryRemote; import org.openbase.bco.registry.template.remote.TemplateRegistryRemote; @@ -93,8 +96,8 @@ public class Registries { * @throws CouldNotPerformException is throw if at least one registry is not available. * @throws InterruptedException is thrown if thread is externally interrupted. */ - public static List getRegistries(final boolean waitForData) throws CouldNotPerformException, InterruptedException { - final List registryList = new ArrayList<>(); + public static List> getRegistries(final boolean waitForData) throws CouldNotPerformException, InterruptedException { + final List> registryList = new ArrayList<>(); registryList.add(getTemplateRegistry(waitForData)); registryList.add(getClassRegistry(waitForData)); registryList.add(getActivityRegistry(waitForData)); @@ -109,7 +112,7 @@ public static List getRegistries(final boolean waitForData) thro * * @throws CouldNotPerformException is throw if at least one registry is not available. */ - public static List getRegistries() throws CouldNotPerformException { + public static List> getRegistries() throws CouldNotPerformException { try { return getRegistries(false); } catch (InterruptedException ex) { @@ -184,6 +187,19 @@ public static TemplateRegistryRemote getTemplateRegistry(final long timeout, fin return registry; } + /** + * Returns an initialized and activated remote registry. + * + * @return the remote registry instance. + * + * @throws NotAvailableException + */ + public static MessageRegistryRemote getMessageRegistry(final long timeout, final TimeUnit timeUnit) throws CouldNotPerformException, InterruptedException { + final MessageRegistryRemote registry = CachedMessageRegistryRemote.getRegistry(); + registry.waitForData(timeout, timeUnit); + return registry; + } + /** * Returns an initialized and activated remote registry. @@ -218,6 +234,17 @@ public static TemplateRegistryRemote getTemplateRegistry() throws NotAvailableEx return CachedTemplateRegistryRemote.getRegistry(); } + /** + * Returns an initialized and activated remote registry. + * + * @return the remote registry instance. + * + * @throws NotAvailableException + */ + public static MessageRegistryRemote getMessageRegistry() throws NotAvailableException { + return CachedMessageRegistryRemote.getRegistry(); + } + /** * Returns an initialized and activated remote registry. * @@ -302,6 +329,27 @@ public static TemplateRegistryRemote getTemplateRegistry(final boolean waitForDa } } + /** + * Returns an initialized and activated remote registry. + * + * @param waitForData defines if this call should block until the registry data is available. + * + * @return the remote registry instance. + * + * @throws NotAvailableException + * @throws InterruptedException is thrown if thread is externally interrupted. + */ + public static MessageRegistryRemote getMessageRegistry(final boolean waitForData) throws CouldNotPerformException, InterruptedException { + try { + if (waitForData) { + CachedMessageRegistryRemote.getRegistry().waitForData(); + } + return CachedMessageRegistryRemote.getRegistry(); + } catch (CouldNotPerformException ex) { + throw new NotAvailableException(MessageRegistry.class, ex); + } + } + /** * Method shutdown all registry instances. *

@@ -315,6 +363,7 @@ public static void shutdown() { CachedActivityRegistryRemote.shutdown(); CachedClassRegistryRemote.shutdown(); CachedTemplateRegistryRemote.shutdown(); + CachedMessageRegistryRemote.shutdown(); } public static void prepare() throws CouldNotPerformException { @@ -322,15 +371,17 @@ public static void prepare() throws CouldNotPerformException { CachedActivityRegistryRemote.prepare(); CachedClassRegistryRemote.prepare(); CachedTemplateRegistryRemote.prepare(); + CachedMessageRegistryRemote.prepare(); } public static Future requestData() { try { return FutureProcessor.allOf( - CachedUnitRegistryRemote.getRegistry().requestData(), - CachedActivityRegistryRemote.getRegistry().requestData(), - CachedClassRegistryRemote.getRegistry().requestData(), - CachedTemplateRegistryRemote.getRegistry().requestData() + CachedUnitRegistryRemote.getRegistry().requestData(), + CachedActivityRegistryRemote.getRegistry().requestData(), + CachedClassRegistryRemote.getRegistry().requestData(), + CachedTemplateRegistryRemote.getRegistry().requestData(), + CachedMessageRegistryRemote.getRegistry().requestData() ); } catch (NotAvailableException ex) { return FutureProcessor.canceledFuture(ex); @@ -348,6 +399,7 @@ public static void waitForData() throws CouldNotPerformException, InterruptedExc CachedClassRegistryRemote.waitForData(); CachedActivityRegistryRemote.waitForData(); CachedUnitRegistryRemote.waitForData(); + CachedMessageRegistryRemote.waitForData(); } /** @@ -361,6 +413,7 @@ public static void waitForData(long timeout, TimeUnit timeUnit) throws CouldNotP CachedClassRegistryRemote.waitForData(timeout, timeUnit); CachedActivityRegistryRemote.waitForData(timeout, timeUnit); CachedUnitRegistryRemote.waitForData(timeout, timeUnit); + CachedMessageRegistryRemote.waitForData(timeout, timeUnit); } public static boolean isDataAvailable() { @@ -368,7 +421,8 @@ public static boolean isDataAvailable() { return CachedUnitRegistryRemote.getRegistry().isDataAvailable() && CachedTemplateRegistryRemote.getRegistry().isDataAvailable() && CachedClassRegistryRemote.getRegistry().isDataAvailable() - && CachedActivityRegistryRemote.getRegistry().isDataAvailable(); + && CachedActivityRegistryRemote.getRegistry().isDataAvailable() + && CachedMessageRegistryRemote.getRegistry().isDataAvailable(); } catch (NotAvailableException ex) { // at least one remote is not available. } @@ -386,6 +440,7 @@ public static void reinitialize() throws CouldNotPerformException, InterruptedEx CachedClassRegistryRemote.reinitialize(); CachedActivityRegistryRemote.reinitialize(); CachedUnitRegistryRemote.reinitialize(); + CachedMessageRegistryRemote.reinitialize(); } /** @@ -401,6 +456,7 @@ public static void waitUntilReady() throws InterruptedException, CouldNotPerform CachedClassRegistryRemote.waitUntilReady(); CachedActivityRegistryRemote.waitUntilReady(); CachedUnitRegistryRemote.waitUntilReady(); + CachedMessageRegistryRemote.waitUntilReady(); } /** @@ -619,7 +675,8 @@ private static Object invokeMethod(final String methodName, final MessageOrBuild final AbstractRemoteClient remote = getRegistryRemoteByType(messageOrBuilder); Method method = remote.getClass().getMethod(methodName, classes); return method.invoke(remote, parameters); - } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | + InvocationTargetException ex) { throw new CouldNotPerformException("Could not invoke method[" + methodName + "] for type[" + messageOrBuilder.getDescriptorForType().getName() + "]", ex); } } @@ -632,25 +689,16 @@ private static String getMethodNameForType(final MessageOrBuilder messageOrBuild return messageOrBuilder.getDescriptorForType().getName(); } - private static AbstractRemoteClient getRegistryRemoteByType(final MessageOrBuilder messageOrBuilder) throws CouldNotPerformException { - switch (messageOrBuilder.getDescriptorForType().getName()) { - case "UnitConfig": - return getUnitRegistry(); - case "DeviceClass": - case "AgentClass": - case "AppClass": - case "GatewayClass": - return getClassRegistry(); - case "UnitTemplate": - case "ServiceTemplate": - case "ActivityTemplate": - return getTemplateRegistry(); - case "ActivityConfig": - return getActivityRegistry(); - default: - throw new NotAvailableException("Registry remote for type [" + messageOrBuilder.getDescriptorForType().getName() + "]"); - - } + private static AbstractRemoteClient getRegistryRemoteByType(final MessageOrBuilder messageOrBuilder) throws CouldNotPerformException { + return switch (messageOrBuilder.getDescriptorForType().getName()) { + case "UnitConfig" -> getUnitRegistry(); + case "DeviceClass", "AgentClass", "AppClass", "GatewayClass" -> getClassRegistry(); + case "UnitTemplate", "ServiceTemplate", "ActivityTemplate" -> getTemplateRegistry(); + case "ActivityConfig" -> getActivityRegistry(); + case "UserMessage" -> getMessageRegistry(); + default -> + throw new NotAvailableException("Registry remote for type [" + messageOrBuilder.getDescriptorForType().getName() + "]"); + }; } /** @@ -664,7 +712,7 @@ private static AbstractRemoteClient getRegistryRemoteByType(final MessageOrBuild * * @throws InstantiationException is thrown if the registry is not available. */ - public static UnitRegistryRemote getUnitRegistry(final Class clazz) throws InstantiationException { + public static UnitRegistryRemote getUnitRegistry(final Class clazz) throws InstantiationException { try { return Registries.getUnitRegistry(); } catch (NotAvailableException ex) { diff --git a/module/registry/template-registry/remote/src/main/java/org/openbase/bco/registry/template/remote/CachedTemplateRegistryRemote.java b/module/registry/template-registry/remote/src/main/java/org/openbase/bco/registry/template/remote/CachedTemplateRegistryRemote.java index 4c83bed0ad..b6f966d55c 100644 --- a/module/registry/template-registry/remote/src/main/java/org/openbase/bco/registry/template/remote/CachedTemplateRegistryRemote.java +++ b/module/registry/template-registry/remote/src/main/java/org/openbase/bco/registry/template/remote/CachedTemplateRegistryRemote.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -44,7 +44,7 @@ public class CachedTemplateRegistryRemote { private static final Logger LOGGER = LoggerFactory.getLogger(CachedTemplateRegistryRemote.class); private static final SyncObject REMOTE_LOCK = new SyncObject("CachedTemplateRegistryRemoteLock"); - private static final SyncObject REGISTY_LOCK = new SyncObject("RegistyLock"); + private static final SyncObject REGISTRY_LOCK = new SyncObject("RegistryLock"); private static TemplateRegistryRemote registryRemote; private static volatile boolean shutdown = false; @@ -75,7 +75,7 @@ public static void reinitialize() throws InterruptedException, CouldNotPerformEx try { // only call re-init if the registry was activated and initialized in the first place if (registryRemote != null) { - synchronized (REGISTY_LOCK) { + synchronized (REGISTRY_LOCK) { getRegistry().reinit(REMOTE_LOCK); } } @@ -119,7 +119,7 @@ public static TemplateRegistryRemote getRegistry() throws NotAvailableException return registryRemote; } - synchronized (REGISTY_LOCK) { + synchronized (REGISTRY_LOCK) { if (registryRemote == null) { try { registryRemote = new TemplateRegistryRemote(); diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/UnitRegistryController.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/UnitRegistryController.java index 202cb4cddc..af83257003 100644 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/UnitRegistryController.java +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/UnitRegistryController.java @@ -183,8 +183,8 @@ protected void postInit() throws InitializationException, InterruptedException { // post init loads registries super.postInit(); - // initially fill the alias to id map - // afterwards the {@code AliasMapUpdatePlugin} will manage changes on registering, removing or updating of units + // initially fill the alias to id map afterwards + // the {@code AliasMapUpdatePlugin} will manage changes on registering, removing or updating of units synchronized (aliasIdMapLock) { try { for (ProtoBufFileSynchronizedRegistry registry : unitConfigRegistryList) { @@ -432,6 +432,7 @@ protected void registerDependencies() throws CouldNotPerformException { connectionUnitConfigRegistry.registerDependency(locationUnitConfigRegistry); + agentUnitConfigRegistry.registerDependency(userUnitConfigRegistry); agentUnitConfigRegistry.registerDependency(locationUnitConfigRegistry); agentUnitConfigRegistry.registerDependency(CachedClassRegistryRemote.getRegistry().getAgentClassRemoteRegistry(false)); @@ -439,6 +440,7 @@ protected void registerDependencies() throws CouldNotPerformException { appUnitConfigRegistry.registerDependency(CachedClassRegistryRemote.getRegistry().getAppClassRemoteRegistry(false)); appUnitConfigRegistry.registerDependency(locationUnitConfigRegistry); + appUnitConfigRegistry.registerDependency(userUnitConfigRegistry); } @Override diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/BaseUnitTypeFieldConsistencyHandler.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/BaseUnitTypeFieldConsistencyHandler.java index b21b5cd5be..74d8cc375d 100644 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/BaseUnitTypeFieldConsistencyHandler.java +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/BaseUnitTypeFieldConsistencyHandler.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -43,7 +43,7 @@ public class BaseUnitTypeFieldConsistencyHandler extends AbstractProtoBufRegistr public void processData(String id, IdentifiableMessage entry, ProtoBufMessageMap entryMap, ProtoBufRegistry registry) throws CouldNotPerformException, EntryModification { // filter dal units - if(UnitConfigProcessor.isDalUnit(entry.getMessage().getUnitType())) { + if (UnitConfigProcessor.isDalUnit(entry.getMessage().getUnitType())) { return; } @@ -51,7 +51,7 @@ public void processData(String id, IdentifiableMessage. @@ -34,9 +34,7 @@ import org.openbase.jul.storage.registry.ProtoBufRegistry; import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import java.util.HashMap; import java.util.Locale; -import java.util.Map; /** * Default label consistency handler for units. This consistency handler makes sure that a unit has at least one label @@ -46,12 +44,6 @@ */ public class DefaultUnitLabelConsistencyHandler extends AbstractProtoBufRegistryConsistencyHandler { - private final Map unitMap; - - public DefaultUnitLabelConsistencyHandler() { - this.unitMap = new HashMap<>(); - } - @Override public void processData(final String id, final IdentifiableMessage entry, @@ -90,26 +82,4 @@ protected String generateDefaultLabel(final UnitConfig unitConfig) throws CouldN } return UnitConfigProcessor.getDefaultAlias(unitConfig, "?"); } - - /** - * Generate a key for a label of a unit. - * This key can only exists once. - * Here the location id of the placement of the unit is added to the label to guarantee that this label - * exists only once per location. - * This method can be overwritten by sub classes to guarantee other things. - * - * @param label the label for which the key is generated - * @param languageKey the language key of the label. - * @param unitConfig the unit having the label - * - * @return a key for this label and unit - */ - protected String generateKey(final String label, final String languageKey, final UnitConfig unitConfig) { - return label + "_" + languageKey + "_" + unitConfig.getPlacementConfig().getLocationId(); - } - - @Override - public void reset() { - unitMap.clear(); - } } diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/UnitAliasUniqueVerificationConsistencyHandler.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/UnitAliasUniqueVerificationConsistencyHandler.java deleted file mode 100644 index 1c8701c19a..0000000000 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/UnitAliasUniqueVerificationConsistencyHandler.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.openbase.bco.registry.unit.core.consistency; - -/*- - * #%L - * BCO Registry Unit Core - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.openbase.bco.registry.unit.lib.UnitRegistry; -import org.openbase.jul.exception.*; -import org.openbase.jul.extension.protobuf.IdentifiableMessage; -import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap; -import org.openbase.jul.extension.type.processing.ScopeProcessor; -import org.openbase.jul.storage.registry.AbstractProtoBufRegistryConsistencyHandler; -import org.openbase.jul.storage.registry.EntryModification; -import org.openbase.jul.storage.registry.ProtoBufRegistry; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig.Builder; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -public class UnitAliasUniqueVerificationConsistencyHandler extends AbstractProtoBufRegistryConsistencyHandler { - - private final Map aliasUnitIdMap; - private final UnitRegistry unitRegistry; - - public UnitAliasUniqueVerificationConsistencyHandler(final UnitRegistry unitRegistry) { - this.unitRegistry = unitRegistry; - this.aliasUnitIdMap = new HashMap<>(); - } - - @Override - public void processData(String id, IdentifiableMessage entry, ProtoBufMessageMap entryMap, ProtoBufRegistry registry) throws CouldNotPerformException, EntryModification { - final UnitConfig.Builder unitConfig = entry.getMessage().toBuilder(); - - for (final String alias : unitConfig.getAliasList()) { - if (!aliasUnitIdMap.containsKey(alias.toLowerCase())) { - aliasUnitIdMap.put(alias.toLowerCase(), unitConfig.getId()); - } else { - // if already known check if this unit is owning the alias otherwise throw invalid state - if (!aliasUnitIdMap.get(alias.toLowerCase()).equals(unitConfig.getId())) { - throw new RejectedException("Alias[" + alias.toLowerCase() + "] of Unit[" + ScopeProcessor.generateStringRep(unitConfig.getScope()) + ", " + unitConfig.getId() + "] is already used by Unit[" + aliasUnitIdMap.get(alias.toLowerCase()) + "]"); - } - } - } - } - - @Override - public void reset() { - - // validate known aliases - for (String alias : new ArrayList<>(aliasUnitIdMap.keySet())) { - // remove alias entry if alias is globally unknown. - if (!unitRegistry.containsUnitConfigByAlias(alias)) { - logger.debug("remove alias: " + alias); - aliasUnitIdMap.remove(alias); - } - } - super.reset(); - } - - @Override - public void shutdown() { - aliasUnitIdMap.clear(); - // super call is not performed because those would only call reset() which fails because the unit registry is not responding during shutdown. - } -} diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/UnitAliasUniqueVerificationConsistencyHandler.kt b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/UnitAliasUniqueVerificationConsistencyHandler.kt new file mode 100644 index 0000000000..adcbf00652 --- /dev/null +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/UnitAliasUniqueVerificationConsistencyHandler.kt @@ -0,0 +1,59 @@ +package org.openbase.bco.registry.unit.core.consistency + +import org.openbase.bco.registry.unit.lib.UnitRegistry +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.RejectedException +import org.openbase.jul.extension.protobuf.IdentifiableMessage +import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap +import org.openbase.jul.extension.type.processing.ScopeProcessor +import org.openbase.jul.storage.registry.AbstractProtoBufRegistryConsistencyHandler +import org.openbase.jul.storage.registry.EntryModification +import org.openbase.jul.storage.registry.ProtoBufRegistry +import org.openbase.type.domotic.unit.UnitConfigType +import java.util.* + +class UnitAliasUniqueVerificationConsistencyHandler(private val unitRegistry: UnitRegistry) : + AbstractProtoBufRegistryConsistencyHandler() { + private var aliasUnitIdMap: MutableMap = HashMap() + + @Throws(CouldNotPerformException::class, EntryModification::class) + override fun processData( + id: String, + entry: IdentifiableMessage, + entryMap: ProtoBufMessageMap, + registry: ProtoBufRegistry, + ) { + val unitConfig = entry.message.toBuilder() + + for (alias in unitConfig.aliasList) { + if (!aliasUnitIdMap.containsKey(alias.lowercase(Locale.getDefault()))) { + aliasUnitIdMap[alias.lowercase(Locale.getDefault())] = unitConfig.id + } else { + // if already known check if this unit is owning the alias otherwise throw invalid state + if (aliasUnitIdMap[alias.lowercase(Locale.getDefault())] != unitConfig.id) { + throw RejectedException( + "Alias[" + alias.lowercase(Locale.getDefault()) + "] of Unit[" + ScopeProcessor.generateStringRep( + unitConfig.scope + ) + ", " + unitConfig.id + "] is already used by Unit[" + aliasUnitIdMap[alias.lowercase( + Locale.getDefault() + )] + "]" + ) + } + } + } + } + + override fun reset() { + aliasUnitIdMap = unitRegistry + .getUnitConfigs(true) + .flatMap { config -> config.aliasList.map { alias -> alias.lowercase() to config.id } } + .toMap() + .toMutableMap() + super.reset() + } + + override fun shutdown() { + aliasUnitIdMap.clear() + // super call is not performed because those would only call reset() which fails because the unit registry is not responding during shutdown. + } +} diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupClassGroupConsistencyHandler.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupClassGroupConsistencyHandler.java index 22400d9f0c..a72891e97f 100644 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupClassGroupConsistencyHandler.java +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupClassGroupConsistencyHandler.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -58,7 +58,8 @@ public class AuthorizationGroupClassGroupConsistencyHandler extends AbstractProt public AuthorizationGroupClassGroupConsistencyHandler( final ProtoBufRegistry userRegistry, final ProtoBufRegistry agentRegistry, - final ProtoBufRegistry appRegistry) { + final ProtoBufRegistry appRegistry + ) { this.userRegistry = userRegistry; typeRegistryMap = new HashMap<>(); typeRegistryMap.put(UnitType.AGENT, agentRegistry); @@ -129,7 +130,7 @@ public void processData(final String id, } } - // remove all users which should not longer be part of the group + // remove all users which should no longer be part of the group for (String memberId : authorizationGroupConfig.getMemberIdList()) { if (!userIdList.contains(memberId)) { modification = true; diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupConfigLabelConsistencyHandler.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupConfigLabelConsistencyHandler.java index 30e7044ab5..174bbb1610 100644 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupConfigLabelConsistencyHandler.java +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupConfigLabelConsistencyHandler.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -23,25 +23,10 @@ */ import org.openbase.bco.registry.unit.core.consistency.DefaultUnitLabelConsistencyHandler; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; /** * @author Tamino Huxohl */ public class AuthorizationGroupConfigLabelConsistencyHandler extends DefaultUnitLabelConsistencyHandler { - /** - * Label for authorization groups have to be globally unique. - * Therefore just return the label. - * - * @param label the label for which the key is generated - * @param languageKey the language key of the label. - * @param unitConfig the unit having the label - * - * @return the label - */ - @Override - protected String generateKey(final String label, final String languageKey, final UnitConfig unitConfig) { - return label + "_" + languageKey; - } } diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionLocationConsistencyHandler.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionLocationConsistencyHandler.java deleted file mode 100644 index 8621070923..0000000000 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionLocationConsistencyHandler.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.openbase.bco.registry.unit.core.consistency.connectionconfig; - -/* - * #%L - * BCO Registry Unit Core - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.extension.protobuf.IdentifiableMessage; -import org.openbase.jul.storage.registry.AbstractProtoBufRegistryConsistencyHandler; -import org.openbase.jul.storage.registry.EntryModification; -import org.openbase.jul.storage.registry.ProtoBufFileSynchronizedRegistry; -import org.openbase.type.spatial.PlacementConfigType.PlacementConfig; -import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap; -import org.openbase.jul.storage.registry.ProtoBufRegistry; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.registry.UnitRegistryDataType.UnitRegistryData; - -/** - * - * @author Tamino Huxohl - */ -public class ConnectionLocationConsistencyHandler extends AbstractProtoBufRegistryConsistencyHandler { - - private final ProtoBufFileSynchronizedRegistry locationRegistry; - - public ConnectionLocationConsistencyHandler(ProtoBufFileSynchronizedRegistry locationRegistry) { - this.locationRegistry = locationRegistry; - } - - @Override - public void processData(String id, IdentifiableMessage entry, ProtoBufMessageMap entryMap, ProtoBufRegistry registry) throws CouldNotPerformException, EntryModification { - UnitConfig.Builder connectionUnitConfig = entry.getMessage().toBuilder(); - - String locationId; - try { - locationId = getLowestCommonParentLocation(connectionUnitConfig.getConnectionConfig().getTileIdList(), locationRegistry).getId(); - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not find parent location for connection [" + connectionUnitConfig + "]", ex); - } - if (!locationId.equals(connectionUnitConfig.getPlacementConfig().getLocationId())) { - PlacementConfig.Builder placement = connectionUnitConfig.getPlacementConfig().toBuilder().setLocationId(locationId); - throw new EntryModification(entry.setMessage(connectionUnitConfig.setPlacementConfig(placement), this), this); - } - } - - public static UnitConfig getLowestCommonParentLocation(List locationIds, ProtoBufFileSynchronizedRegistry locationUnitConfigRegistry) throws CouldNotPerformException { - // list containing the pathes from root to each location given by locationIds sorted by the lenght of the path, e.g.: - // home, apartment, hallway, entrance - // home, apartment, outdoor - final List> pathesFromRootMap = new ArrayList<>(); - - // fill the list according to the description above - for (String id : locationIds) { - UnitConfig locationUnitConfig = locationUnitConfigRegistry.getMessage(id); - final List pathFromRootList = new ArrayList<>(); - pathFromRootList.add(locationUnitConfig); - while (!locationUnitConfig.getLocationConfig().getRoot()) { - locationUnitConfig = locationUnitConfigRegistry.getMessage(locationUnitConfig.getPlacementConfig().getLocationId()); - // when adding a location at the front of the list, every entry is moved an index further - pathFromRootList.add(0, locationUnitConfig); - } - pathesFromRootMap.add(pathFromRootList); - } - - // sort the list after their sizes: - // home, apartment, outdoor - // home, apartment, hallway, entrance - pathesFromRootMap.sort(new Comparator>() { - - @Override - public int compare(List o1, List o2) { - return o2.size() - o1.size(); - } - }); - - // find the lowest common parent, e.g. for the example above apartment - // by returning the index before the first elements where the pathes differ - int shortestPath = pathesFromRootMap.get(0).size(); - for (int i = 0; i < shortestPath; ++i) { - String currentId = pathesFromRootMap.get(0).get(i).getId(); - for (int j = 1; j < pathesFromRootMap.size(); ++j) { - if (!pathesFromRootMap.get(j).get(i).getId().equals(currentId)) { - return pathesFromRootMap.get(0).get(i - 1); - } - } - } - - // checking if a lowst common parent exists should not be necessary since a tile cannot be root - return pathesFromRootMap.get(0).get(0); - } - - @Override - public void reset() { - } -} diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionLocationConsistencyHandler.kt b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionLocationConsistencyHandler.kt new file mode 100644 index 0000000000..5a1b8f7e4d --- /dev/null +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionLocationConsistencyHandler.kt @@ -0,0 +1,110 @@ +package org.openbase.bco.registry.unit.core.consistency.connectionconfig + +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.NotAvailableException +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.extension.protobuf.IdentifiableMessage +import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap +import org.openbase.jul.storage.registry.AbstractProtoBufRegistryConsistencyHandler +import org.openbase.jul.storage.registry.EntryModification +import org.openbase.jul.storage.registry.ProtoBufFileSynchronizedRegistry +import org.openbase.jul.storage.registry.ProtoBufRegistry +import org.openbase.type.domotic.registry.UnitRegistryDataType.UnitRegistryData +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig + +/** + * + * @author [Tamino Huxohl](mailto:pleminoq@openbase.org) + */ +class ConnectionLocationConsistencyHandler( + private val locationRegistry: ProtoBufFileSynchronizedRegistry, +) : + AbstractProtoBufRegistryConsistencyHandler() { + @Throws(CouldNotPerformException::class, EntryModification::class) + override fun processData( + id: String, + entry: IdentifiableMessage, + entryMap: ProtoBufMessageMap, + registry: ProtoBufRegistry, + ) { + val connectionUnitConfig = entry.message.toBuilder() + + val locationId: String? = try { + getLowestCommonParentLocation(connectionUnitConfig.connectionConfig.tileIdList, locationRegistry)?.id + } catch (ex: CouldNotPerformException) { + ExceptionPrinter.printHistory( + "Could not find parent location for connection [$connectionUnitConfig]", + ex, + logger + ) + null + } ?: locationRegistry.messages.firstOrNull { it.locationConfig.root }?.id + + locationId?.let { + if (locationId != connectionUnitConfig.placementConfig.locationId) { + val placement = connectionUnitConfig.placementConfig.toBuilder().setLocationId(locationId) + throw EntryModification( + entry.setMessage(connectionUnitConfig.setPlacementConfig(placement), this), + this + ) + } + } + } + + companion object { + fun getLowestCommonParentLocation( + locationIds: List, + locationUnitConfigRegistry: ProtoBufFileSynchronizedRegistry, + ): UnitConfig? { + // list containing the paths from root to each location given by locationIds sorted by the lenght of the path, e.g.: + // home, apartment, hallway, entrance + // home, apartment, outdoor + val pathsFromRootMap: MutableList> = ArrayList() + + // fill the list according to the description above + locationIds + .filter { locationUnitConfigRegistry.contains(it) } + .forEach { id -> + var locationUnitConfig = locationUnitConfigRegistry.getMessage(id) + val pathFromRootList: MutableList = ArrayList() + pathFromRootList.add(locationUnitConfig) + while (!locationUnitConfig.locationConfig.root) { + locationUnitConfig = try { + locationUnitConfigRegistry.getMessage(locationUnitConfig.placementConfig.locationId) + } catch (ex: NotAvailableException) { + continue + } + // when adding a location at the front of the list, every entry is moved an index further + pathFromRootList.add(0, locationUnitConfig) + } + pathsFromRootMap.add(pathFromRootList) + } + + // sort the list after their sizes: + // home, apartment, outdoor + // home, apartment, hallway, entrance + pathsFromRootMap.sortWith { o1: List, o2: List -> o2.size - o1.size } + + // find the lowest common parent, e.g. for the example above apartment + // by returning the index before the first elements where the paths differ + + // return null in case connection is not linked to any locations + if (pathsFromRootMap.isEmpty()) { + return null; + } + + val shortestPath = pathsFromRootMap[0].size + (0 until shortestPath).forEach { i -> + val currentId = pathsFromRootMap[0][i].id + (1 until pathsFromRootMap.size).forEach { j -> + if (pathsFromRootMap[j][i].id != currentId) { + return pathsFromRootMap[0][i - 1] + } + } + } + + // checking if a lowest common parent exists should not be necessary since a tile cannot be root + return pathsFromRootMap[0][0] + } + } +} diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionTilesConsistencyHandler.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionTilesConsistencyHandler.java deleted file mode 100644 index b903a922bc..0000000000 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionTilesConsistencyHandler.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.openbase.bco.registry.unit.core.consistency.connectionconfig; - -/* - * #%L - * BCO Registry Unit Core - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.InvalidStateException; -import org.openbase.jul.extension.protobuf.IdentifiableMessage; -import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap; -import org.openbase.jul.storage.registry.AbstractProtoBufRegistryConsistencyHandler; -import org.openbase.jul.storage.registry.EntryModification; -import org.openbase.jul.storage.registry.ProtoBufFileSynchronizedRegistry; -import org.openbase.jul.storage.registry.ProtoBufRegistry; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.registry.UnitRegistryDataType.UnitRegistryData; -import org.openbase.type.domotic.unit.connection.ConnectionConfigType.ConnectionConfig; -import org.openbase.type.domotic.unit.location.LocationConfigType.LocationConfig; - -/** - * - * @author Tamino Huxohl - */ -public class ConnectionTilesConsistencyHandler extends AbstractProtoBufRegistryConsistencyHandler { - - private final ProtoBufFileSynchronizedRegistry locationRegistry; - - public ConnectionTilesConsistencyHandler(ProtoBufFileSynchronizedRegistry locationRegistry) { - this.locationRegistry = locationRegistry; - } - - @Override - public void processData(String id, IdentifiableMessage entry, ProtoBufMessageMap entryMap, ProtoBufRegistry registry) throws CouldNotPerformException, EntryModification { - UnitConfig.Builder connectionUnitConfig = entry.getMessage().toBuilder(); - ConnectionConfig.Builder connectionConfig = connectionUnitConfig.getConnectionConfigBuilder(); - - - if (connectionConfig.getTileIdList().size() < 2) { - throw new InvalidStateException("Connections must connect at least 2 tiles which is not true for connection [" + entry.getMessage() + "] which is connecting only "+connectionConfig.getTileIdList().size()+"!"); - } - - boolean modification = false; - connectionConfig.clearTileId(); - // remove duplicated entries and location ids that are not tiles - Map tileIds = new HashMap<>(); - for (String tileId : entry.getMessage().getConnectionConfig().getTileIdList()) { - UnitConfig location = null; - if (locationRegistry.contains(tileId)) { - location = locationRegistry.get(tileId).getMessage(); - } - if (location != null && location.getLocationConfig().hasLocationType() && location.getLocationConfig().getLocationType() == LocationConfig.LocationType.TILE) { - tileIds.put(tileId, tileId); - } else { - modification = true; - } - } - connectionConfig.addAllTileId(new ArrayList<>(tileIds.keySet())); - - if (modification) { - throw new EntryModification(entry.setMessage(connectionUnitConfig, this), this); - } - } -} diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionTilesConsistencyHandler.kt b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionTilesConsistencyHandler.kt new file mode 100644 index 0000000000..1effe370a6 --- /dev/null +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/connectionconfig/ConnectionTilesConsistencyHandler.kt @@ -0,0 +1,49 @@ +package org.openbase.bco.registry.unit.core.consistency.connectionconfig + +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.NotAvailableException +import org.openbase.jul.extension.protobuf.IdentifiableMessage +import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap +import org.openbase.jul.storage.registry.AbstractProtoBufRegistryConsistencyHandler +import org.openbase.jul.storage.registry.EntryModification +import org.openbase.jul.storage.registry.ProtoBufFileSynchronizedRegistry +import org.openbase.jul.storage.registry.ProtoBufRegistry +import org.openbase.type.domotic.registry.UnitRegistryDataType +import org.openbase.type.domotic.unit.UnitConfigType +import org.openbase.type.domotic.unit.location.LocationConfigType.LocationConfig.LocationType + +/** + * + * @author [Tamino Huxohl](mailto:pleminoq@openbase.org) + */ +class ConnectionTilesConsistencyHandler(private val locationRegistry: ProtoBufFileSynchronizedRegistry) : + AbstractProtoBufRegistryConsistencyHandler() { + @Throws(CouldNotPerformException::class, EntryModification::class) + override fun processData( + id: String, + entry: IdentifiableMessage, + entryMap: ProtoBufMessageMap, + registry: ProtoBufRegistry, + ) { + val connectionUnitConfig = entry.message.toBuilder() + val connectionConfig = connectionUnitConfig.connectionConfigBuilder + + // remove duplicated entries and location ids that are not tiles + entry.message.connectionConfig.tileIdList + .distinct() + .filter { tileId -> + try { + locationRegistry[tileId].message?.locationConfig?.locationType == LocationType.TILE + } catch (ex: NotAvailableException) { + false + } + } + .let { tileIds -> + if (connectionConfig.tileIdList.toList().sorted() != tileIds.sorted()) { + connectionConfig.clearTileId() + connectionConfig.addAllTileId(tileIds) + throw EntryModification(entry.setMessage(connectionUnitConfig, this), this) + } + } + } +} diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/dalunitconfig/DalUnitLabelConsistencyHandler.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/dalunitconfig/DalUnitLabelConsistencyHandler.java index 2d9184c432..0da1b8910e 100644 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/dalunitconfig/DalUnitLabelConsistencyHandler.java +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/dalunitconfig/DalUnitLabelConsistencyHandler.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -36,10 +36,10 @@ import org.openbase.jul.storage.registry.ProtoBufFileSynchronizedRegistry; import org.openbase.jul.storage.registry.ProtoBufRegistry; import org.openbase.jul.storage.registry.Registry; -import org.openbase.type.language.LabelType.Label; import org.openbase.type.domotic.registry.UnitRegistryDataType.UnitRegistryData; import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; import org.openbase.type.domotic.unit.device.DeviceClassType.DeviceClass; +import org.openbase.type.language.LabelType.Label; import java.util.HashMap; import java.util.Map; @@ -126,18 +126,4 @@ public void processData(final String id, // make sure that label exists and are unique per location per unit type super.processData(id, entry, entryMap, registry); } - - /** - * Make sure that the label is unique per unit type and per location. - * - * @param label the label for which the key is generated - * @param languageKey the language key of the label. - * @param unitConfig the unit having the label - * - * @return a key unique per unit type per location - */ - @Override - protected String generateKey(final String label, final String languageKey, final UnitConfig unitConfig) { - return label + "_" + languageKey + "_" + unitConfig.getUnitType().name() + "_" + unitConfig.getPlacementConfig().getLocationId(); - } } diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/locationconfig/LocationTypeConsistencyHandler.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/locationconfig/LocationTypeConsistencyHandler.java deleted file mode 100644 index aeb9a02eae..0000000000 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/locationconfig/LocationTypeConsistencyHandler.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.openbase.bco.registry.unit.core.consistency.locationconfig; - -/*- - * #%L - * BCO Registry Unit Core - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ -import org.openbase.bco.registry.lib.util.LocationUtils; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.printer.ExceptionPrinter; -import org.openbase.jul.exception.printer.LogLevel; -import org.openbase.jul.extension.protobuf.IdentifiableMessage; -import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap; -import org.openbase.jul.storage.registry.AbstractProtoBufRegistryConsistencyHandler; -import org.openbase.jul.storage.registry.EntryModification; -import org.openbase.jul.storage.registry.ProtoBufRegistry; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.location.LocationConfigType.LocationConfig; -import org.openbase.type.domotic.unit.location.LocationConfigType.LocationConfig.LocationType; - -/** - * - * @author Tamino Huxohl - */ -public class LocationTypeConsistencyHandler extends AbstractProtoBufRegistryConsistencyHandler { - - @Override - public void processData(String id, IdentifiableMessage entry, ProtoBufMessageMap entryMap, ProtoBufRegistry registry) throws CouldNotPerformException, EntryModification { - UnitConfig.Builder locationUnit = entry.getMessage().toBuilder(); - LocationConfig.Builder locationConfig = locationUnit.getLocationConfigBuilder(); - - if (!locationConfig.hasLocationType()) { - try { - locationConfig.setLocationType(LocationUtils.detectLocationType(entry.getMessage(), registry)); - throw new EntryModification(entry.setMessage(locationUnit, this), this); - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("The locationType of location[" + locationUnit.getLabel() + "] has to be defined manually", ex); - } - } else { - try { - LocationType detectedType = LocationUtils.detectLocationType(entry.getMessage(), registry); - if (detectedType != locationConfig.getLocationType()) { - locationConfig.setLocationType(detectedType); - throw new EntryModification(entry.setMessage(locationUnit, this), this); - } - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Could not detect locationType for location[" + locationUnit.getLabel() + "] with current type [" + locationConfig.getLocationType().name() + "]", ex, logger, LogLevel.DEBUG); - } - } - } -} diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/locationconfig/LocationTypeConsistencyHandler.kt b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/locationconfig/LocationTypeConsistencyHandler.kt new file mode 100644 index 0000000000..8313e6ddd9 --- /dev/null +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/locationconfig/LocationTypeConsistencyHandler.kt @@ -0,0 +1,59 @@ +package org.openbase.bco.registry.unit.core.consistency.locationconfig + +import org.openbase.bco.registry.lib.util.LocationUtils +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.exception.printer.LogLevel +import org.openbase.jul.extension.protobuf.IdentifiableMessage +import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap +import org.openbase.jul.storage.registry.AbstractProtoBufRegistryConsistencyHandler +import org.openbase.jul.storage.registry.EntryModification +import org.openbase.jul.storage.registry.ProtoBufRegistry +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig +import org.openbase.type.domotic.unit.location.LocationConfigType.LocationConfig.LocationType + +/** + * + * @author [Tamino Huxohl](mailto:pleminoq@openbase.org) + */ +class LocationTypeConsistencyHandler : + AbstractProtoBufRegistryConsistencyHandler() { + @Throws(CouldNotPerformException::class, EntryModification::class) + override fun processData( + id: String, + entry: IdentifiableMessage, + entryMap: ProtoBufMessageMap, + registry: ProtoBufRegistry, + ) { + val locationUnit = entry.message.toBuilder() + val locationConfig = locationUnit.locationConfigBuilder + + val detectedType: LocationType = LocationUtils.detectLocationType(entry.message, registry) + + if (!locationConfig.hasLocationType()) { + try { + locationConfig.setLocationType(detectedType) + throw EntryModification(entry.setMessage(locationUnit, this), this) + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException( + "The locationType of location[" + locationUnit.label + "] has to be defined manually", + ex + ) + } + } else { + try { + if (detectedType != locationConfig.locationType) { + locationConfig.setLocationType(detectedType) + throw EntryModification(entry.setMessage(locationUnit, this), this) + } + } catch (ex: CouldNotPerformException) { + ExceptionPrinter.printHistory( + "Could not detect locationType for location[" + locationUnit.label + "] with current type [" + locationConfig.locationType.name + "]", + ex, + logger, + LogLevel.DEBUG + ) + } + } + } +} diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/userconfig/UserUnitLabelConsistencyHandler.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/userconfig/UserUnitLabelConsistencyHandler.java index a13c3e1880..06858c1869 100644 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/userconfig/UserUnitLabelConsistencyHandler.java +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/userconfig/UserUnitLabelConsistencyHandler.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -37,7 +37,9 @@ public class UserUnitLabelConsistencyHandler extends DefaultUnitLabelConsistency * Generate a default user name. * * @param unitConfig the unit config for which a label is generated + * * @return username (firstName lastName) + * * @throws CouldNotPerformException if values in the user config are missing */ @Override @@ -66,17 +68,4 @@ protected String generateDefaultLabel(UnitConfig unitConfig) throws CouldNotPerf } return label; } - - /** - * Return the label to make sure user label are unique. - * - * @param label the label for which the key is generated - * @param languageKey the language key of the label. - * @param unitConfig the unit having the label - * @return the label - */ - @Override - protected String generateKey(String label, final String languageKey, UnitConfig unitConfig) { - return label + "_" + languageKey; - } } diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/plugin/PublishUnitTransformationRegistryPlugin.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/plugin/PublishUnitTransformationRegistryPlugin.java index ff005d586d..4b570fe117 100644 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/plugin/PublishUnitTransformationRegistryPlugin.java +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/plugin/PublishUnitTransformationRegistryPlugin.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -58,6 +58,16 @@ protected void publishTransformation(IdentifiableMessage. diff --git a/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.java b/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.java deleted file mode 100644 index 4a87d2f2db..0000000000 --- a/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.java +++ /dev/null @@ -1,347 +0,0 @@ -package org.openbase.bco.registry.unit.lib.auth; - -/*- - * #%L - * BCO Registry Unit Library - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import com.google.protobuf.ProtocolStringList; -import org.openbase.bco.authentication.lib.AuthPair; -import org.openbase.bco.authentication.lib.AuthenticationBaseData; -import org.openbase.bco.authentication.lib.AuthorizationHelper; -import org.openbase.bco.authentication.lib.AuthorizationHelper.PermissionType; -import org.openbase.bco.registry.lib.util.UnitConfigProcessor; -import org.openbase.bco.registry.template.lib.TemplateRegistry; -import org.openbase.bco.registry.template.remote.CachedTemplateRegistryRemote; -import org.openbase.bco.registry.unit.lib.UnitRegistry; -import org.openbase.jul.exception.*; -import org.openbase.jul.exception.MultiException.ExceptionStack; -import org.openbase.type.domotic.authentication.AuthorizationTokenType.AuthorizationToken; -import org.openbase.type.domotic.authentication.AuthorizationTokenType.AuthorizationToken.PermissionRule; -import org.openbase.type.domotic.authentication.PermissionType.Permission; -import org.openbase.type.domotic.authentication.UserClientPairType.UserClientPair; -import org.openbase.type.domotic.communication.UserMessageType.UserMessage; -import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServiceType; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashSet; -import java.util.Set; - -/** - * @author Tamino Huxohl - */ -public class AuthorizationWithTokenHelper { - - private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationWithTokenHelper.class); - - /** - * Verify an authorization token. This is done in two steps. - * By checking if all entered ids match either a UnitConfig, ServiceTemplate or UnitTemplate. - * If the id matches a unit config it is checked if the user defined in the token has at least as many permissions - * for the unit as the token would grant. - * - * @param authorizationToken the authorization token that is checked - * @param unitRegistry registry used to resolve authorization groups and locations to check permissions - * - * @throws CouldNotPerformException thrown if the token is invalid - */ - public static void verifyAuthorizationToken(final AuthorizationToken authorizationToken, final UnitRegistry unitRegistry) throws CouldNotPerformException { - try { - for (final PermissionRule permissionRule : authorizationToken.getPermissionRuleList()) { - // make sure the unit with the given id exists - final UnitConfig unitConfig; - try { - unitConfig = unitRegistry.getUnitConfigById(permissionRule.getUnitId()); - - // make sure the unit template with the given id exists - if (permissionRule.hasUnitTemplateId()) { - CachedTemplateRegistryRemote.getRegistry().getUnitTemplateById(permissionRule.getUnitTemplateId()); - } - - // make sure the service template with the given id exists - if (permissionRule.hasServiceTemplateId()) { - CachedTemplateRegistryRemote.getRegistry().getServiceTemplateById(permissionRule.getServiceTemplateId()); - } - } catch (CouldNotPerformException ex) { - throw new RejectedException("Invalid unit id, service template id or unit template id", ex); - } - - // a filter reduces permissions so it can be granted anyways - if (permissionRule.getFilter()) { - continue; - } - - // evaluate the permissions the given user has for the unit defined in the token - final Permission permission = AuthorizationHelper.getPermission(unitConfig, authorizationToken.getUserId(), unitRegistry.getAuthorizationGroupMap(), unitRegistry.getLocationMap()); - - // reject the token if the user tries to give more permissions than he has - if (!AuthorizationHelper.isSubPermission(permission, permissionRule.getPermission())) { - throw new RejectedException("User[" + authorizationToken.getUserId() + "] has not enough permissions to create an authorizationToken with permissions[" + permissionRule.getPermission() + "] for unit[" + UnitConfigProcessor.getDefaultAlias(unitConfig, "?") + "]"); - } - } - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could verify access token", ex); - } - } - - /** - * Perform a permission check by validating that the actor is either the receiver or the sender of the message. - * - * @param authenticationBaseData the authentication data including who is authenticated and tokens used for authorization - * @param userMessage the user message for which permissions are checked - * @param permissionType the permission type which is checked - * @param unitRegistry unit registry used to resolve ids - * - * @return a string representing the authorized user, this is either just the username of the authenticated user - * or the username of the authenticated user followed by the username of the issuer of the authorization token - * - * @throws CouldNotPerformException thrown if the user does not have permissions or if the check fails - */ - public static AuthPair canDo( - final AuthenticationBaseData authenticationBaseData, - final UserMessage userMessage, - final PermissionType permissionType, - final UnitRegistry unitRegistry) throws CouldNotPerformException { - - try { - // validate sender - return canDo(authenticationBaseData, unitRegistry.getUnitConfigById(userMessage.getSenderId()), permissionType, unitRegistry, null, null); - } catch (CouldNotPerformException ex) { - ExceptionStack exceptionStack = null; - exceptionStack = MultiException.push(AuthorizationWithTokenHelper.class, ex, exceptionStack); - - // validate receiver if sender validation failed. - try { - return canDo(authenticationBaseData, unitRegistry.getUnitConfigById(userMessage.getRecipientId()), permissionType, unitRegistry, null, null); - } catch (CouldNotPerformException exx) { - exceptionStack = MultiException.push(AuthorizationWithTokenHelper.class, exx, exceptionStack); - MultiException.checkAndThrow(() -> "Permission denied!", exceptionStack); - } - } - throw new FatalImplementationErrorException("ExceptionStack empty in error case.", AuthorizationWithTokenHelper.class); - } - - /** - * Perform a permission check according to {@link #canDo(AuthenticationBaseData, UnitConfig, PermissionType, UnitRegistry, UnitType, ServiceType)} - * by ignoring unit type and service type permissions. - * - * @param authenticationBaseData the authentication data including who is authenticated and tokens used for authorization - * @param unitConfig the unit config for which permissions are checked - * @param permissionType the permission type which is checked - * @param unitRegistry unit registry used to resolve ids - * - * @return a string representing the authorized user, this is either just the username of the authenticated user - * or the username of the authenticated user followed by the username of the issuer of the authorization token - * - * @throws CouldNotPerformException thrown if the user does not have permissions or if the check fails - */ - public static AuthPair canDo( - final AuthenticationBaseData authenticationBaseData, - final UnitConfig unitConfig, - final PermissionType permissionType, - final UnitRegistry unitRegistry) throws CouldNotPerformException { - return canDo(authenticationBaseData, unitConfig, permissionType, unitRegistry, null, null); - } - - - /** - * Perform a permission check for authentication data including tokens. - * - * @param authenticationBaseData the authentication data including who is authenticated and tokens used for authorization - * @param unitConfig the unit config for which permissions are checked - * @param permissionType the permission type which is checked - * @param unitRegistry unit registry used to resolve ids - * @param unitType the unit type for which is checked if the authorization tokens gives permissions for it, if it is null - * it will be ignored - * @param serviceType the service type for which is checked if the authorization tokens gives permissions for it, if it is null - * it will be ignored - * - * @return a string representing the authorized user, this is either just the username of the authenticated user - * or the username of the authenticated user followed by the username of the issuer of the authorization token - * - * @throws CouldNotPerformException thrown if the user does not have permissions or if the check fails - */ - public static AuthPair canDo( - final AuthenticationBaseData authenticationBaseData, - final UnitConfig unitConfig, - final PermissionType permissionType, - final UnitRegistry unitRegistry, - final UnitType unitType, - final ServiceType serviceType) throws CouldNotPerformException { - try { - // resolve the responsible user - UserClientPair userClientPair; - if (authenticationBaseData == null) { - // authentication data is not given so use null to check for other permissions - userClientPair = UserClientPair.getDefaultInstance(); - } else { - if (authenticationBaseData.getAuthenticationToken() != null) { - // authentication token is set so use it as the responsible user - userClientPair = UserClientPair.newBuilder().setUserId(authenticationBaseData.getAuthenticationToken().getUserId()).build(); - } else { - // use the user that is authenticated for the request - userClientPair = authenticationBaseData.getUserClientPair(); - } - } - - // check if authenticated user has needed permissions - if (AuthorizationHelper.canDo(unitConfig, userClientPair.getUserId(), unitRegistry.getAuthorizationGroupMap(), unitRegistry.getLocationMap(), permissionType)) { - return new AuthPair(userClientPair, userClientPair.getUserId()); - } - if (AuthorizationHelper.canDo(unitConfig, userClientPair.getClientId(), unitRegistry.getAuthorizationGroupMap(), unitRegistry.getLocationMap(), permissionType)) { - return new AuthPair(userClientPair, userClientPair.getClientId()); - } - - try { - // test if user is part of the admin group - final ProtocolStringList memberIdList = unitRegistry.getUnitConfigByAlias(UnitRegistry.ADMIN_GROUP_ALIAS).getAuthorizationGroupConfig().getMemberIdList(); - if (memberIdList.contains(userClientPair.getUserId())) { - return new AuthPair(userClientPair, userClientPair.getUserId()); - } - if (memberIdList.contains(userClientPair.getClientId())) { - return new AuthPair(userClientPair, userClientPair.getClientId()); - } - } catch (NotAvailableException ex) { - // continue with the checks, admin group is not available - } - - // authenticated user does not have permissions so check if the authorization token grants them - if (authenticationBaseData != null && authenticationBaseData.getAuthorizationToken() != null) { - final AuthorizationToken authorizationToken = authenticationBaseData.getAuthorizationToken(); - // verify that the authorization token is valid - verifyAuthorizationToken(authorizationToken, unitRegistry); - - // verify if the token grants the necessary permissions - return authorizedByToken(authorizationToken, userClientPair, unitConfig, permissionType, unitRegistry, unitType, serviceType); - } - String userRepresentation = userClientPair.getUserId(); - if (!userRepresentation.isEmpty()) { - userRepresentation += "@"; - } - userRepresentation += userClientPair.getClientId(); - if (userRepresentation.isEmpty()) { - userRepresentation = "Other"; - } - throw new PermissionDeniedException("User[" + userRepresentation + "] " + permissionType.name().toLowerCase() + " permission denied!"); - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not verify permissions for unit[" + UnitConfigProcessor.getDefaultAlias(unitConfig, "?") + "]", ex); - } - } - - private static AuthPair authorizedByToken( - final AuthorizationToken authorizationToken, - final UserClientPair userClientPair, - final UnitConfig unitConfig, - final PermissionType permissionType, - final UnitRegistry unitRegistry, - final UnitType unitType, - final ServiceType serviceType) throws CouldNotPerformException { - // verify if the token grants the necessary permissions - final TemplateRegistry templateRegistry = CachedTemplateRegistryRemote.getRegistry(); - final Set grantingPermissionSet = new HashSet<>(); - final Set filteringPermissionSet = new HashSet<>(); - for (final PermissionRule permissionRule : authorizationToken.getPermissionRuleList()) { - if (permissionRule.getFilter()) { - filteringPermissionSet.add(permissionRule); - } else { - grantingPermissionSet.add(permissionRule); - } - } - - boolean granted = false; - for (final PermissionRule permissionRule : grantingPermissionSet) { - if (permitted(unitConfig, permissionRule, unitRegistry, templateRegistry, serviceType, unitType, permissionType)) { - granted = true; - break; - } - } - - if (!granted) { - throw new PermissionDeniedException("Authorization token does not grant the necessary permissions"); - } - - for (final PermissionRule permissionRule : filteringPermissionSet) { - if (permitted(unitConfig, permissionRule, unitRegistry, templateRegistry, serviceType, unitType, permissionType)) { - throw new PermissionDeniedException("Authorization token does not grant the necessary permissions"); - } - } - - // build the auth pair - return new AuthPair(userClientPair, authorizationToken.getUserId()); - } - - private static boolean permitted(final UnitConfig unitConfig, - final PermissionRule permissionRule, - final UnitRegistry unitRegistry, - final TemplateRegistry templateRegistry, - final ServiceType serviceType, - final UnitType unitType, - final PermissionType permissionType) throws CouldNotPerformException { - // the permission would not grant these permissions anyway so return false - // this is done first so that rejections are handled fast - if (!AuthorizationHelper.permitted(permissionRule.getPermission(), permissionType)) { - return false; - } - // test if the unit id in the permission rule matches the unit config - if (!permissionRule.getUnitId().equals(unitConfig.getId())) { - // it does not so test if the unit id belongs to a location - final UnitConfig locationToCheck = unitRegistry.getUnitConfigById(permissionRule.getUnitId()); - // if it is not a location then the permission rule do not permit what is asked - if (locationToCheck.getUnitType() != UnitType.LOCATION) { - return false; - } - // if the location does not contain the given unit config the permission rule does not permit it - if (!containsUnit(unitConfig, locationToCheck, unitRegistry)) { - return false; - } - } - // if the given service type is defined and the rule contains a service type which does not match return false - if (serviceType != null - && serviceType != ServiceType.UNKNOWN - && permissionRule.hasServiceTemplateId() - && templateRegistry.getServiceTemplateById(permissionRule.getServiceTemplateId()).getServiceType() != serviceType) { - return false; - } - // if the given unit type is defined and the rule contains a unit type which does not match return false - return unitType == null - || unitType == UnitType.UNKNOWN - || !permissionRule.hasUnitTemplateId() - || templateRegistry.getUnitTemplateById(permissionRule.getUnitTemplateId()).getUnitType() == unitType; - } - - private static boolean containsUnit(final UnitConfig unitConfig, final UnitConfig locationToCheck, final UnitRegistry unitRegistry) throws CouldNotPerformException { - UnitConfig location = unitRegistry.getUnitConfigById(unitConfig.getPlacementConfig().getLocationId()); - if (location.getId().equals(locationToCheck.getId())) { - return true; - } - - while (!location.getLocationConfig().getRoot()) { - location = unitRegistry.getUnitConfigById(location.getPlacementConfig().getLocationId()); - if (location.getId().equals(locationToCheck.getId())) { - return true; - } - } - - return false; - } -} diff --git a/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt b/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt new file mode 100644 index 0000000000..d2b4c4cab7 --- /dev/null +++ b/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt @@ -0,0 +1,419 @@ +package org.openbase.bco.registry.unit.lib.auth + +import org.openbase.bco.authentication.lib.AuthPair +import org.openbase.bco.authentication.lib.AuthenticationBaseData +import org.openbase.bco.authentication.lib.AuthorizationHelper +import org.openbase.bco.registry.lib.util.UnitConfigProcessor +import org.openbase.bco.registry.template.lib.TemplateRegistry +import org.openbase.bco.registry.template.remote.CachedTemplateRegistryRemote +import org.openbase.bco.registry.unit.lib.UnitRegistry +import org.openbase.jul.exception.* +import org.openbase.jul.exception.MultiException.ExceptionStack +import org.openbase.type.domotic.authentication.AuthorizationTokenType +import org.openbase.type.domotic.authentication.UserClientPairType +import org.openbase.type.domotic.communication.UserMessageType +import org.openbase.type.domotic.service.ServiceTemplateType +import org.openbase.type.domotic.unit.UnitConfigType +import org.openbase.type.domotic.unit.UnitTemplateType +import org.slf4j.LoggerFactory +import java.util.* + +/*- +* #%L +* BCO Registry Unit Library +* %% +* Copyright (C) 2014 - 2021 openbase.org +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ /** + * @author [Tamino Huxohl](mailto:pleminoq@openbase.org) + */ +object AuthorizationWithTokenHelper { + private val LOGGER = LoggerFactory.getLogger(AuthorizationWithTokenHelper::class.java) + + /** + * Verify an authorization token. This is done in two steps. + * By checking if all entered ids match either a UnitConfig, ServiceTemplate or UnitTemplate. + * If the id matches a unit config it is checked if the user defined in the token has at least as many permissions + * for the unit as the token would grant. + * + * @param authorizationToken the authorization token that is checked + * @param unitRegistry registry used to resolve authorization groups and locations to check permissions + * + * @throws CouldNotPerformException thrown if the token is invalid + */ + @JvmStatic + @Throws(CouldNotPerformException::class) + fun verifyAuthorizationToken( + authorizationToken: AuthorizationTokenType.AuthorizationToken, + unitRegistry: UnitRegistry, + ) { + try { + for (permissionRule in authorizationToken.permissionRuleList) { + // make sure the unit with the given id exists + val unitConfig: UnitConfigType.UnitConfig + try { + unitConfig = unitRegistry.getUnitConfigById(permissionRule.getUnitId()) + + // make sure the unit template with the given id exists + if (permissionRule.hasUnitTemplateId()) { + CachedTemplateRegistryRemote.getRegistry() + .getUnitTemplateById(permissionRule.getUnitTemplateId()) + } + + // make sure the service template with the given id exists + if (permissionRule.hasServiceTemplateId()) { + CachedTemplateRegistryRemote.getRegistry() + .getServiceTemplateById(permissionRule.getServiceTemplateId()) + } + } catch (ex: CouldNotPerformException) { + throw RejectedException("Invalid unit id, service template id or unit template id", ex) + } + + // a filter reduces permissions so it can be granted anyways + if (permissionRule.filter) { + continue + } + + // evaluate the permissions the given user has for the unit defined in the token + val permission = AuthorizationHelper.getPermission( + unitConfig, + authorizationToken.getUserId(), + unitRegistry.getAuthorizationGroupMap(), + unitRegistry.getLocationMap() + ) + + // reject the token if the user tries to give more permissions than he has + if (!AuthorizationHelper.isSubPermission(permission, permissionRule.permission)) { + throw RejectedException( + "User[" + authorizationToken.getUserId() + "] has not enough permissions to create an authorizationToken with permissions[" + permissionRule.permission + "] for unit[" + UnitConfigProcessor.getDefaultAlias( + unitConfig, + "?" + ) + "]" + ) + } + } + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException("Could verify access token", ex) + } + } + + /** + * Perform a permission check by validating that the actor is either the receiver or the sender of the message. + * + * @param authenticationBaseData the authentication data including who is authenticated and tokens used for authorization + * @param userMessage the user message for which permissions are checked + * @param permissionType the permission type which is checked + * @param unitRegistry unit registry used to resolve ids + * + * @return a string representing the authorized user, this is either just the username of the authenticated user + * or the username of the authenticated user followed by the username of the issuer of the authorization token + * + * @throws CouldNotPerformException thrown if the user does not have permissions or if the check fails + */ + @JvmStatic + @Throws(CouldNotPerformException::class) + fun canDo( + authenticationBaseData: AuthenticationBaseData?, + userMessage: UserMessageType.UserMessage, + permissionType: AuthorizationHelper.PermissionType, + unitRegistry: UnitRegistry, + ): AuthPair = try { + // validate sender + canDo( + authenticationBaseData, + unitRegistry.getUnitConfigById(userMessage.senderId), + permissionType, + unitRegistry, + null, + null + ) + } catch (ex: CouldNotPerformException) { + var exceptionStack: ExceptionStack? = null + exceptionStack = MultiException.push(AuthorizationWithTokenHelper::class.java, ex, exceptionStack) + + // validate receiver if sender validation failed. + try { + canDo( + authenticationBaseData, + unitRegistry.getUnitConfigById(userMessage.recipientId), + permissionType, + unitRegistry, + null, + null + ) + } catch (exx: CouldNotPerformException) { + exceptionStack = MultiException.push(AuthorizationWithTokenHelper::class.java, exx, exceptionStack) + + userMessage.conditionList.firstNotNullOfOrNull { condition -> + try { + canDo( + authenticationBaseData, + unitRegistry.getUnitConfigById(condition.unitId), + permissionType, + unitRegistry, + null, + null + ) + } catch (exxx: CouldNotPerformException) { + exceptionStack = MultiException.push(AuthorizationWithTokenHelper::class.java, exxx, exceptionStack) + null + } ?: let { + MultiException.checkAndThrow({ "Permission denied!" }, exceptionStack) + null + } + } + } + } ?: throw FatalImplementationErrorException( + "ExceptionStack empty in error case.", + AuthorizationWithTokenHelper::class.java + ) + + /** + * Perform a permission check for authentication data including tokens. + * + * @param authenticationBaseData the authentication data including who is authenticated and tokens used for authorization + * @param unitConfig the unit config for which permissions are checked + * @param permissionType the permission type which is checked + * @param unitRegistry unit registry used to resolve ids + * @param unitType the unit type for which is checked if the authorization tokens gives permissions for it, if it is null + * it will be ignored + * @param serviceType the service type for which is checked if the authorization tokens gives permissions for it, if it is null + * it will be ignored + * + * @return a string representing the authorized user, this is either just the username of the authenticated user + * or the username of the authenticated user followed by the username of the issuer of the authorization token + * + * @throws CouldNotPerformException thrown if the user does not have permissions or if the check fails + */ + @JvmStatic + @JvmOverloads + @Throws(CouldNotPerformException::class) + fun canDo( + authenticationBaseData: AuthenticationBaseData?, + unitConfig: UnitConfigType.UnitConfig, + permissionType: AuthorizationHelper.PermissionType, + unitRegistry: UnitRegistry, + unitType: UnitTemplateType.UnitTemplate.UnitType? = null, + serviceType: ServiceTemplateType.ServiceTemplate.ServiceType? = null, + ): AuthPair { + try { + // resolve the responsible user + val userClientPair: UserClientPairType.UserClientPair + userClientPair = if (authenticationBaseData == null) { + // authentication data is not given so use null to check for other permissions + UserClientPairType.UserClientPair.getDefaultInstance() + } else { + if (authenticationBaseData.authenticationToken != null) { + // authentication token is set so use it as the responsible user + UserClientPairType.UserClientPair.newBuilder() + .setUserId(authenticationBaseData.authenticationToken.getUserId()).build() + } else { + // use the user that is authenticated for the request + authenticationBaseData.userClientPair + } + } + + // check if authenticated user has needed permissions + if (AuthorizationHelper.canDo( + unitConfig, + userClientPair.getUserId(), + unitRegistry.getAuthorizationGroupMap(), + unitRegistry.getLocationMap(), + permissionType + ) + ) { + return AuthPair(userClientPair, userClientPair.getUserId()) + } + if (AuthorizationHelper.canDo( + unitConfig, + userClientPair.getClientId(), + unitRegistry.getAuthorizationGroupMap(), + unitRegistry.getLocationMap(), + permissionType + ) + ) { + return AuthPair(userClientPair, userClientPair.getClientId()) + } + try { + // test if user is part of the admin group + val memberIdList = + unitRegistry.getUnitConfigByAlias(UnitRegistry.ADMIN_GROUP_ALIAS).authorizationGroupConfig.memberIdList + if (memberIdList.contains(userClientPair.getUserId())) { + return AuthPair(userClientPair, userClientPair.getUserId()) + } + if (memberIdList.contains(userClientPair.getClientId())) { + return AuthPair(userClientPair, userClientPair.getClientId()) + } + } catch (ex: NotAvailableException) { + // continue with the checks, admin group is not available + } + + // authenticated user does not have permissions so check if the authorization token grants them + if (authenticationBaseData != null && authenticationBaseData.authorizationToken != null) { + val authorizationToken = authenticationBaseData.authorizationToken + // verify that the authorization token is valid + verifyAuthorizationToken(authorizationToken, unitRegistry) + + // verify if the token grants the necessary permissions + return authorizedByToken( + authorizationToken, + userClientPair, + unitConfig, + permissionType, + unitRegistry, + unitType, + serviceType + ) + } + var userRepresentation = userClientPair.getUserId() + if (!userRepresentation.isEmpty()) { + userRepresentation += "@" + } + userRepresentation += userClientPair.getClientId() + if (userRepresentation.isEmpty()) { + userRepresentation = "Other" + } + throw PermissionDeniedException("User[" + userRepresentation + "] " + permissionType.name.lowercase(Locale.getDefault()) + " permission denied!") + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException( + "Could not verify permissions for unit[" + UnitConfigProcessor.getDefaultAlias( + unitConfig, + "?" + ) + "]", ex + ) + } + } + + @Throws(CouldNotPerformException::class) + private fun authorizedByToken( + authorizationToken: AuthorizationTokenType.AuthorizationToken, + userClientPair: UserClientPairType.UserClientPair, + unitConfig: UnitConfigType.UnitConfig, + permissionType: AuthorizationHelper.PermissionType, + unitRegistry: UnitRegistry, + unitType: UnitTemplateType.UnitTemplate.UnitType?, + serviceType: ServiceTemplateType.ServiceTemplate.ServiceType?, + ): AuthPair { + // verify if the token grants the necessary permissions + val templateRegistry: TemplateRegistry = CachedTemplateRegistryRemote.getRegistry() + val grantingPermissionSet: MutableSet = HashSet() + val filteringPermissionSet: MutableSet = HashSet() + for (permissionRule in authorizationToken.permissionRuleList) { + if (permissionRule.filter) { + filteringPermissionSet.add(permissionRule) + } else { + grantingPermissionSet.add(permissionRule) + } + } + var granted = false + for (permissionRule in grantingPermissionSet) { + if (permitted( + unitConfig, + permissionRule, + unitRegistry, + templateRegistry, + serviceType, + unitType, + permissionType + ) + ) { + granted = true + break + } + } + if (!granted) { + throw PermissionDeniedException("Authorization token does not grant the necessary permissions") + } + for (permissionRule in filteringPermissionSet) { + if (permitted( + unitConfig, + permissionRule, + unitRegistry, + templateRegistry, + serviceType, + unitType, + permissionType + ) + ) { + throw PermissionDeniedException("Authorization token does not grant the necessary permissions") + } + } + + // build the auth pair + return AuthPair(userClientPair, authorizationToken.getUserId()) + } + + @Throws(CouldNotPerformException::class) + private fun permitted( + unitConfig: UnitConfigType.UnitConfig, + permissionRule: AuthorizationTokenType.AuthorizationToken.PermissionRule, + unitRegistry: UnitRegistry, + templateRegistry: TemplateRegistry, + serviceType: ServiceTemplateType.ServiceTemplate.ServiceType?, + unitType: UnitTemplateType.UnitTemplate.UnitType?, + permissionType: AuthorizationHelper.PermissionType, + ): Boolean { + // the permission would not grant these permissions anyway so return false + // this is done first so that rejections are handled fast + if (!AuthorizationHelper.permitted(permissionRule.permission, permissionType)) { + return false + } + // test if the unit id in the permission rule matches the unit config + if (permissionRule.getUnitId() != unitConfig.getId()) { + // it does not so test if the unit id belongs to a location + val locationToCheck = unitRegistry.getUnitConfigById(permissionRule.getUnitId()) + // if it is not a location then the permission rule do not permit what is asked + if (locationToCheck.getUnitType() != UnitTemplateType.UnitTemplate.UnitType.LOCATION) { + return false + } + // if the location does not contain the given unit config the permission rule does not permit it + if (!containsUnit(unitConfig, locationToCheck, unitRegistry)) { + return false + } + } + // if the given service type is defined and the rule contains a service type which does not match return false + return if (serviceType != null && serviceType != ServiceTemplateType.ServiceTemplate.ServiceType.UNKNOWN && permissionRule.hasServiceTemplateId() && templateRegistry.getServiceTemplateById( + permissionRule.getServiceTemplateId() + ) + .getServiceType() != serviceType + ) { + false + } else unitType == null || unitType == UnitTemplateType.UnitTemplate.UnitType.UNKNOWN || !permissionRule.hasUnitTemplateId() || templateRegistry.getUnitTemplateById( + permissionRule.getUnitTemplateId() + ).getUnitType() == unitType + // if the given unit type is defined and the rule contains a unit type which does not match return false + } + + @Throws(CouldNotPerformException::class) + private fun containsUnit( + unitConfig: UnitConfigType.UnitConfig, + locationToCheck: UnitConfigType.UnitConfig, + unitRegistry: UnitRegistry, + ): Boolean { + var location = unitRegistry.getUnitConfigById(unitConfig.placementConfig.getLocationId()) + if (location.getId() == locationToCheck.getId()) { + return true + } + while (!location.locationConfig.root) { + location = unitRegistry.getUnitConfigById(location.placementConfig.getLocationId()) + if (location.getId() == locationToCheck.getId()) { + return true + } + } + return false + } +} diff --git a/module/registry/unit-registry/remote/src/main/java/org/openbase/bco/registry/unit/remote/CachedUnitRegistryRemote.java b/module/registry/unit-registry/remote/src/main/java/org/openbase/bco/registry/unit/remote/CachedUnitRegistryRemote.java index 9398cdc99d..8db0f0d283 100644 --- a/module/registry/unit-registry/remote/src/main/java/org/openbase/bco/registry/unit/remote/CachedUnitRegistryRemote.java +++ b/module/registry/unit-registry/remote/src/main/java/org/openbase/bco/registry/unit/remote/CachedUnitRegistryRemote.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -27,7 +27,6 @@ import org.openbase.jul.exception.*; import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.iface.Shutdownable; -import org.openbase.jul.iface.Shutdownable.ShutdownDaemon; import org.openbase.jul.schedule.SyncObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,7 +44,7 @@ public class CachedUnitRegistryRemote { private static final Logger LOGGER = LoggerFactory.getLogger(CachedUnitRegistryRemote.class); private static final SyncObject REMOTE_LOCK = new SyncObject("CachedUnitRegistryRemoteLock"); - private static final SyncObject REGISTY_LOCK = new SyncObject("RegistyLock"); + private static final SyncObject REGISTRY_LOCK = new SyncObject("RegistryLock"); private static UnitRegistryRemote registryRemote; private static volatile boolean shutdown = false; @@ -76,7 +75,7 @@ public static void reinitialize() throws InterruptedException, CouldNotPerformEx try { // only call re-init if the registry was activated and initialized in the first place if (registryRemote != null) { - synchronized (REGISTY_LOCK) { + synchronized (REGISTRY_LOCK) { getRegistry().reinit(REMOTE_LOCK); } } @@ -120,7 +119,7 @@ public static UnitRegistryRemote getRegistry() throws NotAvailableException { return registryRemote; } - synchronized (REGISTY_LOCK) { + synchronized (REGISTRY_LOCK) { if (registryRemote == null) { try { registryRemote = new UnitRegistryRemote(); diff --git a/module/registry/unit-registry/test/src/test/java/org/openbase/bco/registry/unit/test/LocationRegistryTest.java b/module/registry/unit-registry/test/src/test/java/org/openbase/bco/registry/unit/test/LocationRegistryTest.java index 943f9011b1..e854c29b85 100644 --- a/module/registry/unit-registry/test/src/test/java/org/openbase/bco/registry/unit/test/LocationRegistryTest.java +++ b/module/registry/unit-registry/test/src/test/java/org/openbase/bco/registry/unit/test/LocationRegistryTest.java @@ -210,19 +210,9 @@ public void testConnectionTilesConsistency() throws Exception { // create a connection with only one tile id UnitConfig.Builder failingConnectionConfig = getConnectionUnitBuilder("Failing connection"); failingConnectionConfig.getConnectionConfigBuilder().setConnectionType(ConnectionType.DOOR).addTileId(tile1.getId()); - try { - // set exception printer to quit because an exception is expected - ExceptionPrinter.setBeQuit(Boolean.TRUE); - // try to register the connection which should fail - Registries.getUnitRegistry().registerUnitConfig(failingConnectionConfig.build()).get(); - // fail of no exception has been thrown - fail("Registered connection with less than one tile"); - } catch (ExecutionException ex) { - // if an execution exception is thrown the connection could not be registered - } finally { - // reset quit flag from exception printer - ExceptionPrinter.setBeQuit(Boolean.FALSE); - } + // register the connection which should lead to a single tile of the connection + var result = Registries.getUnitRegistry().registerUnitConfig(failingConnectionConfig.build()).get(); + assertEquals(result.getConnectionConfig().getTileIdCount(), 1); // create a new connection with duplicated and fake tile ids UnitConfig.Builder connection = getConnectionUnitBuilder("Test Connection"); @@ -264,7 +254,10 @@ public void testLocationTypeConsistency() throws Exception { assertEquals(LocationType.ZONE, root.getLocationConfig().getLocationType(), "Location type zone has not been recovered for root location"); // register a tile - UnitConfig.Builder tile = Registries.getUnitRegistry().registerUnitConfig(getLocationUnitBuilder(LocationType.TILE, "Tile", root.getId()).build()).get().toBuilder(); + UnitConfig.Builder tile = Registries.getUnitRegistry().registerUnitConfig(getLocationUnitBuilder(LocationType.UNKNOWN, "Tile", root.getId()).build()).get().toBuilder(); + + // make sure that the location has been identified as tile + assertEquals(LocationType.TILE, tile.getLocationConfig().getLocationType(), "Type has not been detected for tile"); // register a location under a tile, therefore it should be inferred to be a region UnitConfig.Builder region = getLocationUnitBuilder("Region"); @@ -274,8 +267,9 @@ public void testLocationTypeConsistency() throws Exception { // now the tile has a zone as its parent and a region as its child, therefore the consistency handler should be // able to recover its type - tile.getLocationConfigBuilder().setLocationType(LocationType.ZONE); + tile.getLocationConfigBuilder().setLocationType(LocationType.UNKNOWN); tile = Registries.getUnitRegistry().updateUnitConfig(tile.build()).get().toBuilder(); + assertFalse(tile.getLocationConfig().getRoot(), "Should not be the new root location"); assertEquals(LocationType.TILE, tile.getLocationConfig().getLocationType(), "Type of tile has not been recovered"); } diff --git a/module/registry/util/src/main/java/org/openbase/bco/registry/mock/MockRegistry.java b/module/registry/util/src/main/java/org/openbase/bco/registry/mock/MockRegistry.java index 4f007b80b3..b8f7d2fded 100644 --- a/module/registry/util/src/main/java/org/openbase/bco/registry/mock/MockRegistry.java +++ b/module/registry/util/src/main/java/org/openbase/bco/registry/mock/MockRegistry.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -31,12 +31,12 @@ import org.openbase.bco.registry.activity.core.ActivityRegistryLauncher; import org.openbase.bco.registry.clazz.core.ClassRegistryLauncher; import org.openbase.bco.registry.lib.jp.JPBCODatabaseDirectory; +import org.openbase.bco.registry.message.core.MessageRegistryLauncher; import org.openbase.bco.registry.remote.Registries; import org.openbase.bco.registry.template.core.TemplateRegistryLauncher; import org.openbase.bco.registry.unit.core.UnitRegistryLauncher; import org.openbase.jps.core.JPService; import org.openbase.jps.exception.JPServiceException; -import org.openbase.jps.preset.JPTestMode; import org.openbase.jps.preset.JPTmpDirectory; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.FatalImplementationErrorException; @@ -45,7 +45,6 @@ import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.exception.printer.LogLevel; import org.openbase.jul.extension.protobuf.IdentifiableMessage; -import org.openbase.jul.extension.protobuf.ProtoBufBuilderProcessor; import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap; import org.openbase.jul.extension.type.processing.LabelProcessor; import org.openbase.jul.processing.StringProcessor; @@ -174,6 +173,7 @@ public class MockRegistry { public static final String USER_NAME = "uSeRnAmE"; public static final String USER_FIRST_NAME = "Max"; public static final String USER_LAST_NAME = "Mustermann"; + public static final Map APP_CLASS_LABEL_ID_MAP = new HashMap<>(); public static final Map AGENT_CLASS_LABEL_ID_MAP = new HashMap<>(); public static final AxisAlignedBoundingBox3DFloat DEFAULT_BOUNDING_BOX = AxisAlignedBoundingBox3DFloat.newBuilder() .setHeight(10) @@ -191,6 +191,7 @@ public class MockRegistry { private static ClassRegistryLauncher classRegistryLauncher; private static TemplateRegistryLauncher templateRegistryLauncher; private static UnitRegistryLauncher unitRegistryLauncher; + private static MessageRegistryLauncher messageRegistryLauncher; protected MockRegistry() throws InstantiationException { try { @@ -263,6 +264,15 @@ protected MockRegistry() throws InstantiationException { } return null; })); + registryStartupTasks.add(GlobalCachedExecutorService.submit(() -> { + try { + messageRegistryLauncher = new MessageRegistryLauncher(); + messageRegistryLauncher.launch().get(); + } catch (CouldNotPerformException ex) { + throw ExceptionPrinter.printHistoryAndReturnThrowable(ex, LOGGER, LogLevel.ERROR); + } + return null; + })); LOGGER.debug("Starting all registries: unit, class, template, activity..."); for (Future task : registryStartupTasks) { while (true) { @@ -283,7 +293,7 @@ protected MockRegistry() throws InstantiationException { Registries.waitForData(); - if(loadTestData) { + if (loadTestData) { registryStartupTasks.add(GlobalCachedExecutorService.submit(() -> { LOGGER.debug("Update serviceTemplates..."); for (MockServiceTemplate mockServiceTemplate : MockServiceTemplate.values()) { @@ -473,7 +483,19 @@ public static UnitConfig.Builder generateAgentConfig(final String agentClassLabe return agentUnitConfig; } + public static UnitConfig.Builder generateAppConfig(final String alias, final String locationAlias) throws CouldNotPerformException { + final UnitConfig.Builder appUnitConfig = UnitConfig.newBuilder().setUnitType(UnitType.APP); + appUnitConfig.getPlacementConfigBuilder().setLocationId(Registries.getUnitRegistry().getUnitConfigByAlias(locationAlias).getId()); + LabelProcessor.addLabel(appUnitConfig.getLabelBuilder(), Locale.ENGLISH, alias); + appUnitConfig.addAlias(alias); + return appUnitConfig; + } + protected void shutdown() { + if (messageRegistryLauncher != null) { + messageRegistryLauncher.shutdown(); + } + if (unitRegistryLauncher != null) { unitRegistryLauncher.shutdown(); } diff --git a/versions.properties b/versions.properties index c4377848b5..34b241fe1c 100644 --- a/versions.properties +++ b/versions.properties @@ -47,9 +47,8 @@ version.com.google.guava..guava=28.0-jre ## # available=31.0-jre ## # available=31.0.1-android ## # available=31.0.1-jre - -version.org.openbase..jul.communication.mqtt.test=3.3-SNAPSHOT -version.org.openbase..jul.transformation=3.3-SNAPSHOT +version.org.openbase..jul.communication.mqtt.test=3.6-SNAPSHOT +version.org.openbase..jul.transformation=3.6-SNAPSHOT version.org.testcontainers..junit-jupiter=1.18.3 version.org.testcontainers..testcontainers=1.18.3 version.org.springframework.boot..spring-boot-starter-webflux=3.1.2 @@ -60,59 +59,59 @@ version.org.openhab.core.bundles..org.openhab.core.io.rest.core=4.0.4 plugin.org.springframework.boot=3.1.2 plugin.io.spring.dependency-management=1.1.2 version.kotlin=1.9.0 -version.org.openbase..jul.communication.controller=3.3-SNAPSHOT +version.org.openbase..jul.communication.controller=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.communication.mqtt=3.3-SNAPSHOT +version.org.openbase..jul.communication.mqtt=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.exception=3.3-SNAPSHOT +version.org.openbase..jul.exception=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.extension.protobuf=3.3-SNAPSHOT +version.org.openbase..jul.extension.protobuf=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.extension.type.processing=3.3-SNAPSHOT +version.org.openbase..jul.extension.type.processing=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.extension.type.storage=3.3-SNAPSHOT +version.org.openbase..jul.extension.type.storage=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.extension.type.transform=3.3-SNAPSHOT +version.org.openbase..jul.extension.type.transform=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.extension.type.util=3.3-SNAPSHOT +version.org.openbase..jul.extension.type.util=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.pattern.launch=3.3-SNAPSHOT +version.org.openbase..jul.pattern.launch=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.pattern.trigger=3.3-SNAPSHOT +version.org.openbase..jul.pattern.trigger=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.processing=3.3-SNAPSHOT +version.org.openbase..jul.processing=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.storage=3.3-SNAPSHOT +version.org.openbase..jul.storage=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.visual.javafx=3.3-SNAPSHOT +version.org.openbase..jul.visual.javafx=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2 -version.org.openbase..jul.visual.swing=3.3-SNAPSHOT +version.org.openbase..jul.visual.swing=3.6-SNAPSHOT ## # available=3.0.0 ## # available=3.0.1 ## # available=3.0.2