diff --git a/.github/workflows/auto-branch-update.yaml b/.github/workflows/auto-branch-update.yaml new file mode 100644 index 0000000000..28e0e7923f --- /dev/null +++ b/.github/workflows/auto-branch-update.yaml @@ -0,0 +1,17 @@ +name: "Autoupdate PR branches" + +on: + push: + branches: + - dev + - "epic/**" + +jobs: + autoupdate: + name: autoupdate + runs-on: ubuntu-22.04 + steps: + - uses: docker://chinthakagodawita/autoupdate-action:v1 + env: + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + MERGE_CONFLICT_ACTION: "ignore" diff --git a/.github/workflows/deploy-docker.yml b/.github/workflows/deploy-docker.yml index 5ac31ca543..4cbc067ce6 100644 --- a/.github/workflows/deploy-docker.yml +++ b/.github/workflows/deploy-docker.yml @@ -11,6 +11,7 @@ on: pull_request: branches: - dev + - beta types: [opened, labeled, synchronize, reopened, ready_for_review] jobs: @@ -33,6 +34,7 @@ jobs: # generate Docker tags based on the following events/attributes tags: | type=schedule + type=raw,value=review,enable=${{ contains(github.event.pull_request.labels.*.name, 'prebuild docker image') }} type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} @@ -82,6 +84,7 @@ jobs: # generate Docker tags based on the following events/attributes tags: | type=schedule + type=raw,value=review,enable=${{ contains(github.event.pull_request.labels.*.name, 'prebuild docker image') }} type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} @@ -115,6 +118,7 @@ jobs: # generate Docker tags based on the following events/attributes tags: | type=schedule + type=raw,value=review,enable=${{ contains(github.event.pull_request.labels.*.name, 'prebuild docker image') }} type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} diff --git a/.github/workflows/publish-jars.yml b/.github/workflows/publish-jars.yml index cde426ae3d..1160bea37a 100644 --- a/.github/workflows/publish-jars.yml +++ b/.github/workflows/publish-jars.yml @@ -13,12 +13,14 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@v2 - - name: Set up Java - uses: actions/setup-java@v2 + - name: "Checkout Branch" + uses: actions/checkout@v3 + - name: "Setup Java" + uses: actions/setup-java@v3 with: - java-version: '17' - distribution: 'adopt' + distribution: 'temurin' + java-version: 17 + cache: 'gradle' - name: Validate Gradle wrapper uses: gradle/wrapper-validation-action@v1 - name: Publish Packages diff --git a/build-docker.sh b/build-docker.sh index 88ba62dc7b..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} are building${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/build.gradle.kts b/build.gradle.kts index fd4a56fb11..a4621fd612 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + plugins { id("io.github.gradle-nexus.publish-plugin") version "1.1.0" } @@ -12,3 +14,10 @@ nexusPublishing { } } } + + +tasks.withType(KotlinCompile::class).all { + kotlinOptions { + jvmTarget = "17" + } +} diff --git a/docker/Dockerfile b/docker/Dockerfile index 5c7958491d..c246598051 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -75,7 +75,8 @@ ENV \ BCO_HOME="/home/bco/data" \ BCO_LOGS="/home/bco/data/log" \ BCO_BINARY="/usr/bin/bco" \ - BCO_OPTIONS="" + MQTT_BROKER="mqtt-broker" \ + BCO_OPTIONS="--host ${MQTT_BROKER}" # Basic build-time metadata as defined at http://label-schema.org LABEL org.label-schema.build-date=$BUILD_DATE \ @@ -111,7 +112,7 @@ ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] # Configure healthcheck # todo: make sure only the registry availability is checks since devices are not maintained by this instance. -HEALTHCHECK --interval=15m --timeout=2m CMD bco-validate >/dev/null || exit 1 +HEALTHCHECK --interval=15m --timeout=2m CMD bco-validate --host ${MQTT_BROKER} > /dev/null || exit 1 # switch to root, let the entrypoint drop back to bco user USER root diff --git a/docker/bco-demo/Dockerfile b/docker/bco-demo/Dockerfile index 1a7a5d11e0..ee63d55f56 100644 --- a/docker/bco-demo/Dockerfile +++ b/docker/bco-demo/Dockerfile @@ -23,7 +23,7 @@ FROM openbaseorg/bco:${BCO_BASE_IMAGE_VERSION} ENV \ JAVA_OPTS="" \ OPENHAB_CONF="/etc/openhab2" \ - BCO_OPTIONS="--db /tmp/bco/db" \ + BCO_OPTIONS="--db /tmp/bco/db --host ${MQTT_BROKER}" \ BCO_MODULE_PREPARE_SCRIPT="bco-module-prepare.sh" # Basic build-time metadata as defined at http://label-schema.org @@ -51,7 +51,7 @@ COPY --from=builder /workspace/db /usr/share/bco/db/ # Configure healthcheck # todo: make sure only device offered by this device manager are checked -HEALTHCHECK --interval=15m --timeout=2m CMD bco-validate >/dev/null || exit 1 +HEALTHCHECK --interval=15m --timeout=2m CMD bco-validate --host ${MQTT_BROKER} >/dev/null || exit 1 # switch to root, let the entrypoint drop back to bco user USER root diff --git a/docker/device-manager/openhab/Dockerfile b/docker/device-manager/openhab/Dockerfile index 16fbd837cc..02ce876f34 100644 --- a/docker/device-manager/openhab/Dockerfile +++ b/docker/device-manager/openhab/Dockerfile @@ -6,7 +6,6 @@ FROM openbaseorg/bco:${BCO_BASE_IMAGE_VERSION} ENV \ JAVA_OPTS="" \ OPENHAB_CONF="/etc/openhab2" \ - BCO_OPTIONS="" \ BCO_MODULE_PREPARE_SCRIPT="bco-module-prepare.sh" # Basic build-time metadata as defined at http://label-schema.org @@ -33,7 +32,7 @@ RUN ln -s /usr/local/bin/${BCO_MODULE_PREPARE_SCRIPT} && \ # Configure healthcheck # todo: make sure only device offered by this device manager are checked -HEALTHCHECK --interval=15m --timeout=2m CMD bco-validate >/dev/null || exit 1 +HEALTHCHECK --interval=15m --timeout=2m CMD bco-validate --host ${MQTT_BROKER} >/dev/null || exit 1 # switch to root, let the entrypoint drop back to bco user USER root diff --git a/gradle.properties b/gradle.properties index 2359f0396c..1c62fbf0fc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -version = 3.1-SNAPSHOT -org.gradle.caching = true -org.gradle.parallel = false -org.gradle.daemon = true -org.gradle.jvmargs = -Xmx2000m +version=3.1-SNAPSHOT +org.gradle.caching=true +org.gradle.parallel=false +org.gradle.daemon=true +org.gradle.jvmargs=-Xmx2000m diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 249e5832f0..943f0cbfa7 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 070cb702f0..17a8ddce2d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index a69d9cb6c2..65dcd68d65 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac diff --git a/gradlew.bat b/gradlew.bat index 53a6b238d4..6689b85bee 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% diff --git a/lib/jul b/lib/jul index c042bac636..57f9689153 160000 --- a/lib/jul +++ b/lib/jul @@ -1 +1 @@ -Subproject commit c042bac636025c440dfb5eaf0cdbd0f3b7f767a2 +Subproject commit 57f9689153d476d593e2fc9cfaad980cddbe67d5 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/build.gradle.kts b/module/api/graphql/build.gradle.kts index da062d47d8..c74a1de7bc 100644 --- a/module/api/graphql/build.gradle.kts +++ b/module/api/graphql/build.gradle.kts @@ -1,7 +1,7 @@ plugins { id("org.openbase.bco") id("org.springframework.boot") - id("io.spring.dependency-management") version "1.0.11.RELEASE" + id("io.spring.dependency-management") version "1.1.2" } dependencies { @@ -12,12 +12,17 @@ dependencies { exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat") } api("org.springframework.boot:spring-boot-starter-jetty:_") - implementation("org.springframework.boot:spring-boot-starter-websocket:_") { + api("org.springframework.boot:spring-boot-starter-websocket:_") { exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat") } api(Spring.boot.webflux) api("org.springframework:spring-webmvc:_") + api("org.eclipse.jetty:jetty-server:11.0.14") + api("jakarta.servlet:jakarta.servlet-api:6.0.0") + + + api(rootProject.files("lib/external/rejoiner-0.5.0-bco.jar")) api(rootProject.files("lib/external/rejoiner-guice-0.5.0-bco.jar")) // disabled since rejoiner is linked locally. @@ -35,7 +40,6 @@ dependencies { api("com.google.inject:guice:_") api("com.google.guava:guava:_") api("net.javacrumbs.future-converter:future-converter-java8-guava:_") - api("org.jmdns:jmdns:_") api(ReactiveX.rxJava2) } diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/BcoApiGraphQlSpringLaunchable.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/BcoApiGraphQlSpringLaunchable.kt index 7873332a77..2ced63f922 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/BcoApiGraphQlSpringLaunchable.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/BcoApiGraphQlSpringLaunchable.kt @@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory import org.springframework.boot.SpringApplication import org.springframework.context.ConfigurableApplicationContext import java.util.* -import javax.jmdns.ServiceInfo /*- * #%L @@ -55,19 +54,19 @@ import javax.jmdns.ServiceInfo LOGGER.info("Start webserver...") context = SpringApplication.run(BcoGraphQlApiSpringBootApplication::class.java, *JPService.getArgs()) LOGGER.info("Advertise graphql service...") - val qualifiedNameMap = HashMap() - qualifiedNameMap[ServiceInfo.Fields.Application] = "http" - qualifiedNameMap[ServiceInfo.Fields.Instance] = "graphql-bco-openbase" - qualifiedNameMap[ServiceInfo.Fields.Subtype] = "graphql" +// val qualifiedNameMap = HashMap() +// qualifiedNameMap[ServiceInfo.Fields.Application] = "http" +// qualifiedNameMap[ServiceInfo.Fields.Instance] = "graphql-bco-openbase" +// qualifiedNameMap[ServiceInfo.Fields.Subtype] = "graphql" val propertyMap = HashMap() propertyMap["bco-uuid"] = UUID.randomUUID().toString() propertyMap["path"] = "graphql" // lookup port - context?.getEnvironment()?.getProperty("server.port")?.toInt()?.let { port -> - // register service advertising - serviceAdvertiser!!.register(qualifiedNameMap, port, 0, 0, false, propertyMap) - } +// context?.getEnvironment()?.getProperty("server.port")?.toInt()?.let { port -> +// // register service advertising +// serviceAdvertiser!!.register(qualifiedNameMap, port, 0, 0, false, propertyMap) +// } } override fun deactivate() { 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 d5ceec9ddc..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 @@ -1,7 +1,9 @@ package org.openbase.bco.api.graphql import com.google.api.graphql.execution.GuavaListenableFutureSupport -import com.google.api.graphql.rejoiner.* +import com.google.api.graphql.rejoiner.GqlInputConverter +import com.google.api.graphql.rejoiner.Schema +import com.google.api.graphql.rejoiner.SchemaProviderModule import com.google.inject.Guice import com.google.inject.Injector import com.google.inject.Key @@ -12,6 +14,10 @@ import graphql.kickstart.execution.context.DefaultGraphQLContext import graphql.kickstart.execution.context.GraphQLKickstartContext import graphql.kickstart.servlet.context.GraphQLServletContextBuilder import graphql.schema.* +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import jakarta.websocket.Session +import jakarta.websocket.server.HandshakeRequest import org.dataloader.DataLoader import org.dataloader.DataLoaderRegistry import org.openbase.bco.api.graphql.batchloader.BCOUnitBatchLoader @@ -27,14 +33,9 @@ import org.openbase.bco.registry.remote.Registries import org.openbase.bco.registry.unit.lib.UnitRegistry import org.openbase.jul.exception.NotAvailableException import org.openbase.type.domotic.unit.UnitFilterType -import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.context.annotation.Bean -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse -import javax.websocket.Session -import javax.websocket.server.HandshakeRequest /*- * #%L @@ -56,7 +57,8 @@ import javax.websocket.server.HandshakeRequest * License along with this program. If not, see * . * #L% - */@SpringBootApplication + */ +@SpringBootApplication open class BcoGraphQlApiSpringBootApplication { private var injector: Injector? = null @@ -83,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() @@ -98,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( @@ -121,89 +130,18 @@ open class BcoGraphQlApiSpringBootApplication { } SubscriptionModule.subscribeUnitConfigs(unitFilter, includeDisabledUnits) }) + .dataFetcher( + FieldCoordinates.coordinates("Subscription", "userMessages"), + DataFetcher { + SubscriptionModule.subscribeUserMessages() + }) .build() - schema = GraphQLSchema.newSchema(schema) + return GraphQLSchema.newSchema(schema) .subscription(builder.build()) .codeRegistry(codeRegistry) .build() - - //final GraphQLObjectType.Builder queryTypeBuilder = GraphQLObjectType.newObject(schema.getQueryType()); - // final GraphQLObjectType.Builder mutationTypeBuilder = GraphQLObjectType.newObject(schema.getMutationType()); - //TODO: can I define that these arguments can not be null as in an SDL - //TODO: would the preferred way be to define these in an sdl? - /*queryTypeBuilder.field(GraphQLFieldDefinition.newFieldDefinition().name("login").type(Scalars.GraphQLString) - .argument(GraphQLArgument.newArgument().name("username").type(GraphQLNonNull.nonNull(Scalars.GraphQLString)).build()) - .argument(GraphQLArgument.newArgument().name("password").type(GraphQLNonNull.nonNull(Scalars.GraphQLString)).build()) - .build()); - mutationTypeBuilder.field(GraphQLFieldDefinition.newFieldDefinition().name("changePassword").type(Scalars.GraphQLBoolean) - .argument(GraphQLArgument.newArgument().name("username").type(GraphQLNonNull.nonNull(Scalars.GraphQLString)).build()) - .argument(GraphQLArgument.newArgument().name("oldPassword").type(GraphQLNonNull.nonNull(Scalars.GraphQLString)).build()) - .argument(GraphQLArgument.newArgument().name("newPassword").type(GraphQLNonNull.nonNull(Scalars.GraphQLString)).build()) - .build()); - - final GraphQLCodeRegistry codeRegistry = GraphQLCodeRegistry.newCodeRegistry(schema.getCodeRegistry()) - .dataFetcher(FieldCoordinates.coordinates(schema.getQueryType().getName(), "login"), new DataFetcher() { - - @Override - public String get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception { - final String username = dataFetchingEnvironment.getArgument("username"); - final String password = dataFetchingEnvironment.getArgument("password"); - - try { - final String userId = Registries.getUnitRegistry().getUserUnitIdByUserName(username); - final SessionManager sessionManager = new SessionManager(); - sessionManager.loginUser(userId, password, false); - AuthenticatedValueType.AuthenticatedValue authenticatedValue = sessionManager.initializeRequest(AuthenticationTokenType.AuthenticationToken.newBuilder().setUserId(userId).build(), null); - String tokenValue = new AuthenticatedValueFuture<>(Registries.getUnitRegistry().requestAuthenticationTokenAuthenticated(authenticatedValue), - String.class, - authenticatedValue.getTicketAuthenticatorWrapper(), - sessionManager).get(ServerError.BCO_TIMEOUT_SHORT, ServerError.BCO_TIMEOUT_TIME_UNIT); - return tokenValue; - } catch (NotAvailableException ex) { - - throw new ArgumentError(ex); - } catch (Throwable ex) { - System.out.println("Which ex is thrown here? " + ex.getClass().getSimpleName() + ", " + ex.getMessage()); - throw new Exception(ex); - } - } - }) - .dataFetcher(FieldCoordinates.coordinates(schema.getMutationType().getName(), "changePassword"), new DataFetcher() { - @Override - public Boolean get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception { - final String username = dataFetchingEnvironment.getArgument("username"); - final String oldPassword = dataFetchingEnvironment.getArgument("oldPassword"); - final String newPassword = dataFetchingEnvironment.getArgument("newPassword"); - - final String userId = Registries.getUnitRegistry().getUserUnitIdByUserName(username); - - final SessionManager sessionManager = new SessionManager(); - sessionManager.loginUser(userId, oldPassword, false); - sessionManager.changePassword(userId, oldPassword, newPassword).get(ServerError.BCO_TIMEOUT_SHORT, ServerError.BCO_TIMEOUT_TIME_UNIT); - - return true; - } - }) - .build(); - - - schema = GraphQLSchema.newSchema(schema) - .query(queryTypeBuilder.build()) - .mutation(mutationTypeBuilder.build()) - .codeRegistry(codeRegistry) - .build();*/return schema } - /*@Bean - public GraphQL graphQL() { - System.out.println("Add exec strategy.."); - return GraphQL.newGraphQL(schema()).subscriptionExecutionStrategy(new SubscriptionExecutionStrategy()).build(); - }*/ - // - // @Bean - // public GraphQL graphQL() { - // return GraphQL.newGraphQL(schemaProvider().getSchema()).build(); - // } @Bean open fun instrumentation(): Instrumentation { return GuavaListenableFutureSupport.listenableFutureInstrumentation() @@ -246,123 +184,5 @@ open class BcoGraphQlApiSpringBootApplication { ) } } - } // @Autowired - - // GraphQLDataFetchers graphQLDataFetchers; - // public static void main(String[] args) throws InterruptedException, CouldNotPerformException { - // String schema = "type Query{hello: String}"; - // - // SchemaParser schemaParser = new SchemaParser(); - // TypeDefinitionRegistry typeDefinitionRegistry = schemaParser.parse(schema); - // - // RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring() - // .type("Query", builder -> builder.dataFetcher("hello", new StaticDataFetcher("world"))) - // .build(); - // - // SchemaGenerator schemaGenerator = new SchemaGenerator(); - // GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring); - // - // GraphQL build = GraphQL.newGraphQL(new GraphQLProvider().buildSchema()).build(); - // ExecutionResult executionResult = build.execute("{bookById(id: \"book-1\"){name}}"); - // - // System.out.println(executionResult.getData().toString()); - // } - /*private static final ImmutableList STATIC_FIELD = - ImmutableList.of(newFieldDefinition().type(Scalars.GraphQLString).name("_").staticValue("-").build()); - - private static GraphQLFieldDefinition convertField( - Descriptors.FieldDescriptor fieldDescriptor, SchemaOptions schemaOptions) { - DataFetcher dataFetcher = new ProtoDataFetcher(fieldDescriptor); - GraphQLFieldDefinition.Builder builder = - newFieldDefinition() - .type(convertType(fieldDescriptor, schemaOptions)) - .dataFetcher(dataFetcher) - .name(fieldDescriptor.getJsonName()); - builder.description(schemaOptions.commentsMap().get(fieldDescriptor.getFullName())); - if (fieldDescriptor.getOptions().hasDeprecated() - && fieldDescriptor.getOptions().getDeprecated()) { - builder.deprecate("deprecated in proto"); - } - return builder.build(); - } - - static GraphQLObjectType convert( - Descriptors.Descriptor descriptor, - GraphQLInterfaceType nodeInterface) { - ImmutableList graphQLFieldDefinitions = - descriptor.getFields().stream() - .map(field -> convertField(field)) - .collect(toImmutableList()); - - // TODO: add back relay support - - // Optional relayId = - // descriptor.getFields().stream() - // .filter(field -> field.getOptions().hasExtension(RelayOptionsProto.relayOptions)) - // .map( - // field -> - // newFieldDefinition() - // .name("id") - // .type(new GraphQLNonNull(GraphQLID)) - // .description("Relay ID") - // .dataFetcher( - // data -> - // new Relay() - // .toGlobalId( - // getReferenceName(descriptor), - // data.getSource().getField(field).toString())) - // .build()) - // .findFirst(); - - // if (relayId.isPresent()) { - // return GraphQLObjectType.newObject() - // .name(getReferenceName(descriptor)) - // .withInterface(nodeInterface) - // .field(relayId.get()) - // .fields( - // graphQLFieldDefinitions - // .stream() - // .map( - // field -> - // field.getName().equals("id") - // ? GraphQLFieldDefinition.newFieldDefinition() - // .name("rawId") - // .description(field.getDescription()) - // .type(field.getType()) - // .dataFetcher(field.getDataFetcher()) - // .build() - // : field) - // .collect(ImmutableList.toImmutableList())) - // .build(); - // } - - return GraphQLObjectType.newObject() - .name(getReferenceName(descriptor)) - .fields(graphQLFieldDefinitions.isEmpty() ? STATIC_FIELD : graphQLFieldDefinitions) - .build(); - } - - static GraphQLEnumType convert( - Descriptors.EnumDescriptor descriptor) { - GraphQLEnumType.Builder builder = GraphQLEnumType.newEnum().name(getReferenceName(descriptor)); - for (Descriptors.EnumValueDescriptor value : descriptor.getValues()) { - builder.value( - value.getName(), - value.getName()); - } - return builder.build(); - } - - / ** Returns the GraphQL name of the supplied proto. */ - /*static String getReferenceName(Descriptors.GenericDescriptor descriptor) { - return CharMatcher.anyOf(".").replaceFrom(descriptor.getFullName(), "_"); - } - - / ** Returns a reference to the GraphQL type corresponding to the supplied proto. */ - /*static GraphQLTypeReference getReference(Descriptors.GenericDescriptor descriptor) { - return new GraphQLTypeReference(getReferenceName(descriptor)); - }*/ - companion object { - private val LOGGER = LoggerFactory.getLogger(BcoGraphQlApiSpringBootApplication::class.java) } } 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/context/BCOGraphQLWebsocketContext.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/BCOGraphQLWebsocketContext.kt index 28519f4c65..9d96a1376a 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/BCOGraphQLWebsocketContext.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/BCOGraphQLWebsocketContext.kt @@ -1,10 +1,10 @@ package org.openbase.bco.api.graphql.context import graphql.kickstart.execution.context.GraphQLKickstartContext +import jakarta.websocket.Session +import jakarta.websocket.server.HandshakeRequest import org.dataloader.DataLoaderRegistry import java.util.* -import javax.websocket.Session -import javax.websocket.server.HandshakeRequest /*- * #%L diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/DefaultBCOGraphQLContext.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/DefaultBCOGraphQLContext.kt index 8353c4cb83..fd07caacb3 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/DefaultBCOGraphQLContext.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/DefaultBCOGraphQLContext.kt @@ -1,8 +1,8 @@ package org.openbase.bco.api.graphql.context +import jakarta.servlet.http.HttpServletRequest import org.dataloader.DataLoaderRegistry import java.util.* -import javax.servlet.http.HttpServletRequest /*- * #%L diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/discovery/ServiceAdvertiser.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/discovery/ServiceAdvertiser.kt index cb4ec55219..a68764e2fd 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/discovery/ServiceAdvertiser.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/discovery/ServiceAdvertiser.kt @@ -1,18 +1,14 @@ package org.openbase.bco.api.graphql.discovery import org.openbase.jps.core.JPService -import org.openbase.jul.exception.CouldNotPerformException import org.openbase.jul.exception.InstantiationException import org.openbase.jul.iface.Shutdownable import org.slf4j.LoggerFactory -import java.io.IOException import java.net.InetAddress import java.net.NetworkInterface import java.net.SocketException import java.net.UnknownHostException import java.util.* -import javax.jmdns.JmDNS -import javax.jmdns.ServiceInfo /*- @@ -36,10 +32,10 @@ import javax.jmdns.ServiceInfo * . * #L% */ class ServiceAdvertiser private constructor() : Shutdownable { - private val domainNameServices: List +// private val domainNameServices: List init { - domainNameServices = ArrayList() +// domainNameServices = ArrayList() // skip advertising in debug mode if (!JPService.debugMode()) { @@ -69,40 +65,40 @@ import javax.jmdns.ServiceInfo } } - @Throws(CouldNotPerformException::class) - fun register( - qualifiedNameMap: HashMap?, - port: Int, - weight: Int, - priority: Int, - persistent: Boolean, - props: Map?, - ): List { - return try { - val serviceInfoList: MutableList = ArrayList() - for (domainNameService in domainNameServices) { - - // Register the service - val serviceInfo = ServiceInfo.create(qualifiedNameMap, port, weight, priority, false, props) - domainNameService.registerService(serviceInfo) - serviceInfoList.add(serviceInfo) - } - serviceInfoList - } catch (ex: IOException) { - throw CouldNotPerformException("Could not register service!", ex) - } - } - - fun deregisterService(serviceInfo: ServiceInfo?) { - for (domainNameService in domainNameServices) { - domainNameService.unregisterService(serviceInfo) - } - } +// @Throws(CouldNotPerformException::class) +// fun register( +// qualifiedNameMap: HashMap?, +// port: Int, +// weight: Int, +// priority: Int, +// persistent: Boolean, +// props: Map?, +// ): List { +// return try { +// val serviceInfoList: MutableList = ArrayList() +// for (domainNameService in domainNameServices) { +// +// // Register the service +// val serviceInfo = ServiceInfo.create(qualifiedNameMap, port, weight, priority, false, props) +// domainNameService.registerService(serviceInfo) +// serviceInfoList.add(serviceInfo) +// } +// serviceInfoList +// } catch (ex: IOException) { +// throw CouldNotPerformException("Could not register service!", ex) +// } +// } + +// fun deregisterService(serviceInfo: ServiceInfo?) { +// for (domainNameService in domainNameServices) { +// domainNameService.unregisterService(serviceInfo) +// } +// } override fun shutdown() { - for (domainNameService in domainNameServices) { - domainNameService.unregisterAllServices() - } +// for (domainNameService in domainNameServices) { +// domainNameService.unregisterAllServices() +// } } companion object { diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/discovery/ServiceDiscoveryDemo.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/discovery/ServiceDiscoveryDemo.kt deleted file mode 100644 index 0d10a4b94f..0000000000 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/discovery/ServiceDiscoveryDemo.kt +++ /dev/null @@ -1,83 +0,0 @@ -package org.openbase.bco.api.graphql.discovery - -import org.openbase.jul.processing.StringProcessor -import java.io.IOException -import java.net.InetAddress -import java.net.UnknownHostException -import java.util.* -import javax.jmdns.JmDNS -import javax.jmdns.ServiceEvent -import javax.jmdns.ServiceListener - -/*- - * #%L - * BCO GraphQL API - * %% - * Copyright (C) 2020 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% - */ object ServiceDiscoveryDemo { - @Throws(InterruptedException::class) - fun disabledMain(args: Array?) { - try { - // Create a JmDNS instance - val jmdns = JmDNS.create(InetAddress.getLocalHost()) - - // Add a service listener - jmdns.addServiceListener("_http._tcp.local.", SampleListener("graphql-bco-openbase")) - - // Wait a bit - Thread.sleep(30000) - } catch (e: UnknownHostException) { - println(e.message) - } catch (e: IOException) { - println(e.message) - } - } - - private class SampleListener : ServiceListener { - private val serviceNameFilter: String? - - constructor(serviceNameFilter: String?) { - this.serviceNameFilter = serviceNameFilter - } - - constructor() { - serviceNameFilter = null - } - - override fun serviceAdded(event: ServiceEvent) {} - override fun serviceRemoved(event: ServiceEvent) { - if (serviceNameFilter == null || event.name == serviceNameFilter) { - println( - "Offline: " + event.name + "@" + event.info.server + ":" + event.info.port + " - " + StringProcessor.transformCollectionToString( - Arrays.asList(*event.info.hostAddresses.clone()), ", " - ) - ) - } - } - - override fun serviceResolved(event: ServiceEvent) { - if (serviceNameFilter == null || event.name == serviceNameFilter) { - println( - "Online: " + event.name + "@" + event.info.server + ":" + event.info.port + " - " + StringProcessor.transformCollectionToString( - Arrays.asList(*event.info.hostAddresses.clone()), ", " - ) - ) - } - } - } -} 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 c3f96ee52a..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,9 +24,14 @@ 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 import org.openbase.type.domotic.unit.UnitFilterType +import org.openbase.type.domotic.unit.UnitTemplateType +import org.openbase.type.domotic.unit.agent.AgentClassType +import org.openbase.type.domotic.unit.app.AppClassType import org.openbase.type.domotic.unit.gateway.GatewayClassType import org.openbase.type.geometry.PoseType import org.openbase.type.language.LabelType @@ -52,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. * @@ -153,11 +162,35 @@ 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 = ImmutableList.copyOf(Registries.getClassRegistry(true).gatewayClasses) + @Query("agentClasses") + @Throws(CouldNotPerformException::class, InterruptedException::class) + fun agentClasses(): ImmutableList = + ImmutableList.copyOf(Registries.getClassRegistry(true).agentClasses) + + @Query("appClasses") + @Throws(CouldNotPerformException::class, InterruptedException::class) + fun appClasses(): ImmutableList = + ImmutableList.copyOf(Registries.getClassRegistry(true).appClasses) + + @Query("unitTemplates") + @Throws(CouldNotPerformException::class, InterruptedException::class) + fun unitTemplates(): ImmutableList = + ImmutableList.copyOf(Registries.getTemplateRegistry(true).unitTemplates) + + @Query("serviceTemplates") + @Throws(CouldNotPerformException::class, InterruptedException::class) + fun serviceTemplates(): ImmutableList = + ImmutableList.copyOf(Registries.getTemplateRegistry(true).serviceTemplates) + @Mutation("updateUnitConfig") @Throws(BCOGraphQLError::class) fun updateUnitConfig( @@ -377,7 +410,7 @@ import java.util.concurrent.* break } } - if (!entry.value.isEmpty()) { + if (entry.value.isNotEmpty()) { metaConfigBuilder.addEntry(entry) } @@ -395,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( @@ -414,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/schema/SchemaModificationsAdd.java b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/schema/SchemaModificationsAdd.java index 054cfb31de..6ac2d03a5a 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/schema/SchemaModificationsAdd.java +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/schema/SchemaModificationsAdd.java @@ -116,6 +116,31 @@ String addLabelBestMatch(UnitTemplate unitTemplate, DataFetchingEnvironment env) return getLabelForContext(unitTemplate.getLabel(), env.getContext()); } + @SchemaModification(addField = "labelString", onType = AgentClass.class) + String addLabelBestMatch(AgentClass agentClass, DataFetchingEnvironment env) { + return getLabelForContext(agentClass.getLabel(), env.getContext()); + } + + @SchemaModification(addField = "labelString", onType = AppClass.class) + String addLabelBestMatch(AppClass appClass, DataFetchingEnvironment env) { + return getLabelForContext(appClass.getLabel(), env.getContext()); + } + + @SchemaModification(addField = "labelString", onType = DeviceClass.class) + String addLabelBestMatch(DeviceClass deviceClass, DataFetchingEnvironment env) { + return getLabelForContext(deviceClass.getLabel(), env.getContext()); + } + + @SchemaModification(addField = "labelString", onType = GatewayClass.class) + String addLabelBestMatch(GatewayClass gatewayClass, DataFetchingEnvironment env) { + return getLabelForContext(gatewayClass.getLabel(), env.getContext()); + } + + @SchemaModification(addField = "labelString", onType = ServiceTemplate.class) + String addLabelBestMatch(ServiceTemplate serviceTemplate, DataFetchingEnvironment env) { + return getLabelForContext(serviceTemplate.getLabel(), env.getContext()); + } + @SchemaModification(addField = "descriptionString", onType = UnitConfig.class) String addDescriptionBestMatch(UnitConfig unitConfig, DataFetchingEnvironment env) { return getTextForContext(unitConfig.getDescription(), env.getContext()); @@ -225,16 +250,6 @@ private UnitConfig resolveParentTile(String locationId) throws NotAvailableExcep } } - @SchemaModification(addField = "labelString", onType = DeviceClass.class) - String addLabelBestMatch(DeviceClass deviceClass, DataFetchingEnvironment env) { - return getLabelForContext(deviceClass.getLabel(), env.getContext()); - } - - @SchemaModification(addField = "labelString", onType = GatewayClass.class) - String addLabelBestMatch(GatewayClass gatewayClass, DataFetchingEnvironment env) { - return getLabelForContext(gatewayClass.getLabel(), env.getContext()); - } - @SchemaModification(addField = "descriptionString", onType = GatewayClass.class) String addDescriptionBestMatch(GatewayClass gatewayClass, DataFetchingEnvironment env) { return getTextForContext(gatewayClass.getDescription(), env.getContext()); diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/AbstractObserverMapper.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/AbstractObserverMapper.kt index a8230f6d6c..602e346f8f 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/AbstractObserverMapper.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/AbstractObserverMapper.kt @@ -26,8 +26,9 @@ import java.util.function.Consumer * License along with this program. If not, see * . * #L% - */ abstract class AbstractObserverMapper : Observer { + */ abstract class AbstractObserverMapper : Observer { private var emitter: ObservableEmitter? = null + @Throws(Exception::class) override fun update(source: S, target: T) { if (emitter == null) { @@ -42,6 +43,7 @@ import java.util.function.Consumer @Throws(Exception::class) abstract fun mapData(source: S, data: T): E + @Throws(CouldNotPerformException::class) open fun doAfterRemoveObserver() { } @@ -51,10 +53,10 @@ import java.util.function.Consumer } companion object { - fun createObservable( + fun createObservable( addObserver: Consumer>, removeObserver: Consumer>, - obs: AbstractObserverMapper + obs: AbstractObserverMapper, ): Observable { var observable = Observable.create { emitter: ObservableEmitter? -> obs.setEmitter(emitter) 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 17e154c215..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,24 +42,25 @@ 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 private val BACKPRESSURE_STRATEGY = BackpressureStrategy.BUFFER @Throws(BCOGraphQLError::class) - fun subscribeUnits(unitFilter: UnitFilter?): Publisher { + 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/api/graphql/src/main/resources/application.yaml b/module/api/graphql/src/main/resources/application.yaml index 486cb8af8a..d24a777ae9 100644 --- a/module/api/graphql/src/main/resources/application.yaml +++ b/module/api/graphql/src/main/resources/application.yaml @@ -17,4 +17,4 @@ graphql: allowed-methods: "*" allowed-headers: "*" # if you want to @ExceptionHandler annotation for custom GraphQLErrors - exception-handlers-enabled: true \ No newline at end of file + exception-handlers-enabled: true diff --git a/module/app/base/build.gradle.kts b/module/app/base/build.gradle.kts index ef00c97fcb..307037bf4b 100644 --- a/module/app/base/build.gradle.kts +++ b/module/app/base/build.gradle.kts @@ -53,7 +53,7 @@ dependencies { api(project(":bco.app.cloud.connector")) api(project(":bco.app.influxdb.connector")) api(project(":bco.api.graphql")) - api("commons-collections:commons-collections:_") + api("org.apache.commons:commons-collections4:_") testImplementation(project(":bco.dal.test")) } diff --git a/module/app/cloudconnector/src/main/java/org/openbase/bco/app/cloudconnector/SocketWrapper.java b/module/app/cloudconnector/src/main/java/org/openbase/bco/app/cloudconnector/SocketWrapper.java index 1973d98d54..45d617d75f 100644 --- a/module/app/cloudconnector/src/main/java/org/openbase/bco/app/cloudconnector/SocketWrapper.java +++ b/module/app/cloudconnector/src/main/java/org/openbase/bco/app/cloudconnector/SocketWrapper.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 * . @@ -25,7 +25,6 @@ import com.google.gson.*; import io.socket.client.Ack; import io.socket.client.IO; -import io.socket.client.IO.Options; import io.socket.client.Manager; import io.socket.client.Socket; import io.socket.engineio.client.Transport; @@ -165,14 +164,15 @@ public void init() throws InitializationException { } }); }); + // add listener to socket events socket.on(Socket.EVENT_CONNECT, objects -> { // when socket is connected LOGGER.info("Socket of user[" + userId + "] connected"); login(); - }).on(Socket.EVENT_MESSAGE, objects -> { - // handle request - handleRequest(objects[0], (Ack) objects[objects.length - 1]); +// }).on(Socket.EVENT_MESSAGE, objects -> { // todo this seems to be not supported anymore +// // handle request +// handleRequest(objects[0], (Ack) objects[objects.length - 1]); }).on(Socket.EVENT_DISCONNECT, objects -> { // reconnection is automatically done by the socket API, just print that disconnected LOGGER.info("Socket of user[" + userId + "] disconnected"); @@ -188,16 +188,8 @@ public void init() throws InitializationException { handleRelocating(objects[0], (Ack) objects[objects.length - 1]); }).on(INTENT_RENAMING, objects -> { handleRenaming(objects[0], (Ack) objects[objects.length - 1]); - }).on(Socket.EVENT_RECONNECT_ATTEMPT, objects -> { - LOGGER.debug("Attempt to reconnect socket of user {}", userId); - }).on(Socket.EVENT_RECONNECT_ERROR, objects -> { - LOGGER.debug("Reconnection error for socket of user {} because {}", userId, objects.length > 0 ? objects[0] : 1); - }).on(Socket.EVENT_RECONNECT_FAILED, objects -> { - LOGGER.debug("Reconnection failed for socket of user {} because {}", userId, objects.length > 0 ? objects[0] : 1); - }).on(Socket.EVENT_RECONNECTING, objects -> { - LOGGER.info("Reconnection event for user {}", userId); - }).on(Socket.EVENT_RECONNECT, objects -> { - LOGGER.info("Socket of user {} reconnected!", userId); + }).on(Socket.EVENT_CONNECT_ERROR, objects -> { + LOGGER.debug("Connection error for socket of user {} because {}", userId, objects.length > 0 ? objects[0] : 1); }); // add observer to registry that triggers sync requests on changes @@ -921,7 +913,7 @@ public void activate() throws CouldNotPerformException { // make sure socket.connect is never called twice since there is still an open issue which can cause a broken connection. // https://github.com/socketio/socket.io-client-java/issues/576 - if(active) { + if (active) { return; } 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/build.gradle.kts b/module/app/util/build.gradle.kts index 46a4ce9566..8ebb23f953 100644 --- a/module/app/util/build.gradle.kts +++ b/module/app/util/build.gradle.kts @@ -125,7 +125,7 @@ dependencies { api(project(":bco.app.influxdb.connector")) api(project(":bco.api.graphql")) api(project(":bco.dal.visual")) - api("commons-collections:commons-collections:_") + api("org.apache.commons:commons-collections4:_") testImplementation(project(":bco.dal.test")) } 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/BCOSystemValidator.java b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOSystemValidator.java index 1234c969b1..62f0ede3db 100644 --- a/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOSystemValidator.java +++ b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOSystemValidator.java @@ -22,7 +22,7 @@ * #L% */ -import org.apache.commons.collections.comparators.BooleanComparator; +import org.apache.commons.collections4.comparators.BooleanComparator; import org.openbase.bco.app.util.launch.jp.JPExitOnError; import org.openbase.bco.app.util.launch.jp.JPWaitForData; import org.openbase.bco.authentication.lib.BCO; 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/core/src/main/java/org/openbase/bco/authentication/core/AuthenticationController.java b/module/authentication/core/src/main/java/org/openbase/bco/authentication/core/AuthenticationController.java index a078fea91a..b004b41907 100644 --- a/module/authentication/core/src/main/java/org/openbase/bco/authentication/core/AuthenticationController.java +++ b/module/authentication/core/src/main/java/org/openbase/bco/authentication/core/AuthenticationController.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 * . @@ -237,7 +237,7 @@ public Future requestTicketGrantingTicket(final UserCli clientCredentials = credentialStore.getCredentials(userClientPair.getClientId()); } - if(userCredentials != null && clientCredentials != null) { + if (userCredentials != null && clientCredentials != null) { if (!userCredentials.getSymmetric() && !clientCredentials.getSymmetric()) { throw new NotSupportedException("Login with two asymmetric keys", AuthenticationController.class); } diff --git a/module/authentication/core/src/main/java/org/openbase/bco/authentication/core/AuthenticatorLauncher.java b/module/authentication/core/src/main/java/org/openbase/bco/authentication/core/AuthenticatorLauncher.java index 233f15398a..c2c0f413a5 100644 --- a/module/authentication/core/src/main/java/org/openbase/bco/authentication/core/AuthenticatorLauncher.java +++ b/module/authentication/core/src/main/java/org/openbase/bco/authentication/core/AuthenticatorLauncher.java @@ -18,19 +18,19 @@ * 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% */ + /** - * * @author Tamino Huxohl */ public class AuthenticatorLauncher extends AbstractLauncher { @@ -50,7 +50,8 @@ protected void loadProperties() { /** * @param args the command line arguments - * @throws java.lang.InterruptedException is thrown when the thread was externally interrupted. + * + * @throws java.lang.InterruptedException is thrown when the thread was externally interrupted. * @throws org.openbase.jul.exception.CouldNotPerformException thrown in case the launcher could not be started. */ public static void main(final String[] args) throws InterruptedException, CouldNotPerformException { 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/lib/src/main/java/org/openbase/bco/authentication/lib/com/AbstractAuthenticatedConfigurableController.java b/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/com/AbstractAuthenticatedConfigurableController.java index 955d2be78d..a728ceddf8 100644 --- a/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/com/AbstractAuthenticatedConfigurableController.java +++ b/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/com/AbstractAuthenticatedConfigurableController.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 * . @@ -32,13 +32,13 @@ import org.openbase.jps.core.JPService; import org.openbase.jps.exception.JPNotAvailableException; import org.openbase.jul.annotation.RPCMethod; +import org.openbase.jul.communication.controller.AbstractConfigurableController; import org.openbase.jul.communication.iface.RPCServer; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.InvalidStateException; import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.exception.printer.LogLevel; -import org.openbase.jul.communication.controller.AbstractConfigurableController; import org.openbase.type.domotic.authentication.AuthenticatedValueType.AuthenticatedValue; import org.openbase.type.domotic.authentication.TicketAuthenticatorWrapperType.TicketAuthenticatorWrapper; import org.openbase.type.domotic.authentication.UserClientPairType.UserClientPair; diff --git a/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/com/AbstractAuthenticatedControllerServer.java b/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/com/AbstractAuthenticatedControllerServer.java index 135e2d5a31..6f5406d6b4 100644 --- a/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/com/AbstractAuthenticatedControllerServer.java +++ b/module/authentication/lib/src/main/java/org/openbase/bco/authentication/lib/com/AbstractAuthenticatedControllerServer.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 * . @@ -31,6 +31,7 @@ import org.openbase.jps.core.JPService; import org.openbase.jps.exception.JPNotAvailableException; import org.openbase.jul.annotation.RPCMethod; +import org.openbase.jul.communication.controller.AbstractControllerServer; import org.openbase.jul.communication.iface.RPCServer; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; @@ -38,7 +39,6 @@ import org.openbase.jul.exception.NotAvailableException; import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.exception.printer.LogLevel; -import org.openbase.jul.communication.controller.AbstractControllerServer; import org.openbase.type.domotic.authentication.AuthenticatedValueType.AuthenticatedValue; import org.openbase.type.domotic.authentication.TicketAuthenticatorWrapperType.TicketAuthenticatorWrapper; import org.openbase.type.domotic.authentication.UserClientPairType.UserClientPair; 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 9d4064d3a7..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 @@ -121,6 +121,7 @@ /** * @param the data type of this unit used for the state synchronization. * @param the builder used to build the unit data instance. + * * @author Divine Threepwood */ public abstract class AbstractUnitController> extends AbstractAuthenticatedConfigurableController implements UnitController { @@ -185,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); } @@ -682,7 +683,9 @@ public Future applyAction(final ActionDescription actionDescr * @param actionId the id of the action retrieved. * @param lockConsumer string identifying the task. Required because this method has to lock the builder setup because * of access to the {@link #scheduledActionList}. + * * @return the action identified by the provided id as described above. + * * @throws NotAvailableException if not action with the provided id could be found. */ public SchedulableAction getActionById(final String actionId, final String lockConsumer) throws NotAvailableException, InterruptedException { @@ -714,6 +717,7 @@ public SchedulableAction getActionById(final String actionId, final String lockC * * @param userId the id of the user whose permissions are checked. * @param action the action checked. + * * @throws PermissionDeniedException if the user has no permissions to modify the provided action. * @throws CouldNotPerformException if the permissions check could not be performed. */ @@ -887,6 +891,7 @@ private int getSchedulingIndex(Action action) throws InterruptedException { * * @return the {@code action} which is ranked highest and which is therefore currently allocating this unit. * If there is no action left to schedule null is returned. + * * @throws CouldNotPerformException is throw in case the scheduling is currently not possible, e.g. because of a system shutdown. */ public Action reschedule() throws CouldNotPerformException, InterruptedException { @@ -898,8 +903,10 @@ public Action reschedule() throws CouldNotPerformException, InterruptedException * If the current action is not finished it will be rejected. * * @param actionToSchedule a new action to schedule. If null it will be ignored. + * * @return the {@code action} which is ranked highest and which is therefore currently allocating this unit. * If there is no action left to schedule null is returned. + * * @throws CouldNotPerformException is throw in case the scheduling is currently not possible, e.g. because of a system shutdown. */ private Action reschedule(final SchedulableAction actionToSchedule) throws CouldNotPerformException, InterruptedException { @@ -1153,6 +1160,7 @@ public void notifyScheduledActionList() throws InterruptedException { * Syncs the action list into the given {@code dataBuilder}. * * @param dataBuilder used to synchronize with. + * * @throws CouldNotPerformException is thrown if the sync failed. */ private void syncActionList(final DB dataBuilder) throws CouldNotPerformException { @@ -1479,7 +1487,9 @@ protected Action getCurrentAction() throws NotAvailableException { * @param serviceState the prototype of the new state. * @param serviceType the service type of the new state. * @param internalBuilder the builder object used to access the currently applied state. + * * @return the computed state. + * * @throws RejectedException in case the state would not change anything compared to the current one. * @throws CouldNotPerformException if the state could not be computed. */ @@ -1764,6 +1774,7 @@ protected void copyResponsibleAction(final ServiceType sourceServiceType, final * * @param internalBuilder The data builder of this unit which already contains the updated state. * @param serviceType The service type which has been updated. + * * @throws InterruptedException is thrown if the thread is externally interrupted. */ protected void applyCustomDataUpdate(DB internalBuilder, ServiceType serviceType) throws InterruptedException { @@ -1851,7 +1862,9 @@ protected Message getServiceState(final ServiceType serviceType, String userId) * otherwise the parent location remote is returned which refers the location where this unit is placed in. * * @param waitForData flag defines if the method should block until the remote is fully synchronized. + * * @return a location remote instance. + * * @throws NotAvailableException is thrown if the location remote is currently not available. * @throws java.lang.InterruptedException is thrown if the current was externally interrupted. */ @@ -1919,6 +1932,7 @@ public void activate() throws InterruptedException, CouldNotPerformException { * * @param serviceType the type of the new service. * @param operationService the service which performes the operation. + * * @throws CouldNotPerformException is thrown if the type of the service is already registered. */ protected void registerOperationService(final ServiceType serviceType, final OperationService operationService) throws CouldNotPerformException { @@ -1952,6 +1966,7 @@ public Map getOperationServiceMap() { * * @param serviceState {@inheritDoc} * @param serviceType {@inheritDoc} + * * @return {@inheritDoc} */ @Override 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/app/AppManagerLauncher.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerLauncher.java index 4f92ab7c43..169b0f857c 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerLauncher.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerLauncher.java @@ -1,7 +1,7 @@ package org.openbase.bco.dal.control.layer.unit.app; -import org.openbase.bco.dal.lib.layer.unit.app.AppManager; import org.openbase.bco.authentication.lib.BCO; +import org.openbase.bco.dal.lib.layer.unit.app.AppManager; import org.openbase.jul.pattern.launch.AbstractLauncher; /* @@ -14,12 +14,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 * . 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/action/RemoteAction.java b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/action/RemoteAction.java index 8f0bcb61ea..0ea35e8516 100644 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/action/RemoteAction.java +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/action/RemoteAction.java @@ -56,6 +56,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.concurrent.*; @@ -73,7 +74,7 @@ public class RemoteAction implements Action { private final ActionParameter actionParameter; private final ObservableImpl actionDescriptionObservable; private final AuthToken authToken; - private final List impactedRemoteActions = new ArrayList<>(); + private final List impactedRemoteActions = Collections.synchronizedList(new ArrayList<>()); private final Observer impactActionObserver = new Observer() { @Override public void update(RemoteAction source, ActionDescription observable) throws Exception { @@ -642,6 +643,11 @@ public boolean isRunning() { */ @Override public boolean isDone() { + + if (futureObservationTask != null && !futureObservationTask.isDone()) { + return false; + } + try { if (getActionDescription().getIntermediary()) { for (final RemoteAction impactedRemoteAction : impactedRemoteActions) { 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 7daeb037d4..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 @@ -10,26 +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 java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - import org.openbase.bco.dal.lib.layer.unit.location.Location; import org.openbase.bco.dal.remote.layer.unit.CustomUnitPool; import org.openbase.jps.core.JPService; -import org.openbase.jul.exception.*; import org.openbase.jul.exception.InstantiationException; +import org.openbase.jul.exception.*; import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.exception.printer.LogLevel; import org.openbase.jul.extension.type.processing.TimestampProcessor; @@ -42,19 +39,22 @@ import org.openbase.type.domotic.state.ButtonStateType.ButtonState; import org.openbase.type.domotic.state.ButtonStateType.ButtonStateOrBuilder; import org.openbase.type.domotic.state.DoorStateType.DoorState; +import org.openbase.type.domotic.state.MotionStateType.MotionState; +import org.openbase.type.domotic.state.MotionStateType.MotionStateOrBuilder; +import org.openbase.type.domotic.state.PresenceStateType.PresenceState; +import org.openbase.type.domotic.state.PresenceStateType.PresenceState.State; +import org.openbase.type.domotic.state.PresenceStateType.PresenceStateOrBuilder; import org.openbase.type.domotic.state.WindowStateType.WindowState; import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType; import org.openbase.type.domotic.unit.connection.ConnectionConfigType.ConnectionConfig.ConnectionType; import org.openbase.type.domotic.unit.location.LocationConfigType.LocationConfig.LocationType; +import org.openbase.type.domotic.unit.location.LocationDataType.LocationData; import org.openbase.type.domotic.unit.location.TileConfigType.TileConfig.TileType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.openbase.type.domotic.state.MotionStateType.MotionState; -import org.openbase.type.domotic.state.MotionStateType.MotionStateOrBuilder; -import org.openbase.type.domotic.state.PresenceStateType.PresenceState; -import org.openbase.type.domotic.state.PresenceStateType.PresenceState.State; -import org.openbase.type.domotic.state.PresenceStateType.PresenceStateOrBuilder; -import org.openbase.type.domotic.unit.location.LocationDataType.LocationData; + +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; /** * @author Divine Threepwood @@ -74,77 +74,72 @@ 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; public PresenceDetector() throws InstantiationException { - try { - this.presenceStateBuilder = PresenceState.newBuilder(); - this.active = false; - this.presenceStateObservable = new ObservableImpl<>(this); - this.presenceTimeout = new Timeout(PRESENCE_TIMEOUT) { - - @Override - public void expired() { - try { - // if motion is still detected just restart the timeout. - if (location.getData().getMotionState().getValue() == MotionState.State.MOTION) { - GlobalCachedExecutorService.submit(() -> { - try { - presenceTimeout.restart(); - } catch (final CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Could not setup presence timeout!", ex, logger); - } - }); - return; - } - updatePresenceState(PresenceState.newBuilder().setValue(PresenceState.State.ABSENT)); - } catch (ShutdownInProgressException ex) { - // skip update on shutdown - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory(new CouldNotPerformException("Could not notify absent by timer!", ex), logger); + this.presenceStateBuilder = PresenceState.newBuilder(); + this.active = false; + this.presenceStateObservable = new ObservableImpl<>(this); + this.presenceTimeout = new Timeout(PRESENCE_TIMEOUT) { + + @Override + public void expired() { + try { + // if motion is still detected just restart the timeout. + if (location.getData().getMotionState().getValue() == MotionState.State.MOTION) { + GlobalCachedExecutorService.submit(() -> { + try { + presenceTimeout.restart(); + } catch (final CouldNotPerformException ex) { + ExceptionPrinter.printHistory("Could not setup presence timeout!", ex, logger); + } + }); + return; } + updatePresenceState(PresenceState.newBuilder().setValue(PresenceState.State.ABSENT)); + } catch (ShutdownInProgressException ex) { + // skip update on shutdown + } catch (CouldNotPerformException ex) { + ExceptionPrinter.printHistory(new CouldNotPerformException("Could not notify absent by timer!", ex), logger); } - }; + } + }; - locationDataObserver = (DataProvider source, LocationData data) -> { - updateMotionState(data.getMotionState()); - }; + locationDataObserver = (DataProvider source, LocationData data) -> { + updateMotionState(data.getMotionState()); + }; - this.buttonUnitPool = new CustomUnitPool(); - this.connectionUnitPool = new CustomUnitPool(); + this.buttonUnitPool = new CustomUnitPool(); + this.connectionUnitPool = new CustomUnitPool(); - this.buttonUnitPool.addServiceStateObserver((source, data) -> { - try { - PresenceDetector.this.updateButtonState((ButtonState) data); - } catch (ClassCastException ex) { - ExceptionPrinter.printHistory("ButtonPool entail incompatible units!", ex, logger); - } - }); - - this.connectionUnitPool.addServiceStateObserver((source, data) -> { - switch (source.getServiceType()) { - case WINDOW_STATE_SERVICE: - updateWindowState((WindowState) data); - break; - case DOOR_STATE_SERVICE: - updateDoorState((DoorState) data); - break; - case PASSAGE_STATE_SERVICE: - // just ignore passage states. - break; - - default: - logger.warn("Invalid connection service update received: " + source.getServiceType().name() + " from " + source + " pool:" + connectionUnitPool.isActive()); - } - }); - - } catch (CouldNotPerformException ex) { - throw new InstantiationException(this, ex); - } + this.buttonUnitPool.addServiceStateObserver((source, data) -> { + try { + PresenceDetector.this.updateButtonState((ButtonState) data); + } catch (ClassCastException ex) { + ExceptionPrinter.printHistory("ButtonPool entail incompatible units!", ex, logger); + } + }); + + this.connectionUnitPool.addServiceStateObserver((source, data) -> { + switch (source.getServiceType()) { + case WINDOW_STATE_SERVICE: + updateWindowState((WindowState) data); + break; + case DOOR_STATE_SERVICE: + updateDoorState((DoorState) data); + break; + case PASSAGE_STATE_SERVICE: + // just ignore passage states. + break; + + default: + logger.warn("Invalid connection service update received: " + source.getServiceType().name() + " from " + source + " pool:" + connectionUnitPool.isActive()); + } + }); } @Override @@ -165,23 +160,23 @@ public void init(final Location location) throws InitializationException, Interr if ((location.getConfig().getLocationConfig().getLocationType() == LocationType.TILE)) { connectionUnitPool.init( - unitConfig -> unitConfig.getUnitType() == UnitType.CONNECTION, - unitConfig -> { - try { - return unitConfig.getConnectionConfig().getTileIdList().contains(location.getId()); - } catch (NotAvailableException ex) { - ExceptionPrinter.printHistory("Could not resolve location id within connection filter operation.", ex, logger); - return false; - } - }, - unitConfig -> { - try { - return location.getConfig().getLocationConfig().getTileConfig().getTileType() != TileType.OUTDOOR || unitConfig.getConnectionConfig().getConnectionType() != ConnectionType.WINDOW; - } catch (NotAvailableException ex) { - ExceptionPrinter.printHistory("Could not resolve location id within connection filter operation.", ex, logger); - return false; - } - }); + unitConfig -> unitConfig.getUnitType() == UnitType.CONNECTION, + unitConfig -> { + try { + return unitConfig.getConnectionConfig().getTileIdList().contains(location.getId()); + } catch (NotAvailableException ex) { + ExceptionPrinter.printHistory("Could not resolve location id within connection filter operation.", ex, logger); + return false; + } + }, + unitConfig -> { + try { + return location.getConfig().getLocationConfig().getTileConfig().getTileType() != TileType.OUTDOOR || unitConfig.getConnectionConfig().getConnectionType() != ConnectionType.WINDOW; + } catch (NotAvailableException ex) { + ExceptionPrinter.printHistory("Could not resolve location id within connection filter operation.", ex, logger); + return false; + } + }); } } catch (CouldNotPerformException ex) { throw new InitializationException(this, ex); 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.java b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPool.java deleted file mode 100644 index 1fa9bb6bb6..0000000000 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPool.java +++ /dev/null @@ -1,374 +0,0 @@ -package org.openbase.bco.dal.remote.layer.unit; - -/*- - * #%L - * BCO DAL Remote - * %% - * 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.Message; -import org.openbase.bco.dal.lib.layer.service.ServiceStateProvider; -import org.openbase.bco.dal.lib.layer.unit.Unit; -import org.openbase.bco.dal.lib.layer.unit.UnitRemote; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.bco.registry.unit.lib.filter.UnitConfigFilterImpl; -import org.openbase.jul.exception.InstantiationException; -import org.openbase.jul.exception.*; -import org.openbase.jul.exception.printer.ExceptionPrinter; -import org.openbase.jul.extension.protobuf.IdentifiableMessage; -import org.openbase.jul.extension.protobuf.ProtobufListDiff; -import org.openbase.jul.iface.Manageable; -import org.openbase.jul.pattern.Filter; -import org.openbase.jul.pattern.ObservableImpl; -import org.openbase.jul.pattern.Observer; -import org.openbase.jul.pattern.provider.DataProvider; -import org.openbase.jul.storage.registry.RemoteControllerRegistry; -import org.openbase.type.domotic.registry.UnitRegistryDataType.UnitRegistryData; -import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServiceType; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig.Builder; -import org.openbase.type.domotic.unit.UnitFilterType.UnitFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; -import java.util.Map.Entry; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -public class CustomUnitPool implements Manageable>> { - - private static final Logger LOGGER = LoggerFactory.getLogger(CustomUnitPool.class); - - private final ReentrantReadWriteLock UNIT_REMOTE_REGISTRY_LOCK = new ReentrantReadWriteLock(); - private final Observer, UnitRegistryData> unitRegistryDataObserver; - private final Observer unitDataObserver; - private final Observer serviceStateObserver; - private final RemoteControllerRegistry> unitRemoteRegistry; - private final ProtobufListDiff unitConfigDiff; - private final Set> filterSet; - private final ObservableImpl, Message> unitDataObservable; - private final ObservableImpl, Message> serviceStateObservable; - private volatile boolean active; - - public CustomUnitPool() throws InstantiationException { - try { - this.filterSet = new HashSet<>(); - this.unitConfigDiff = new ProtobufListDiff<>(); - this.unitRemoteRegistry = new RemoteControllerRegistry<>(); - this.unitRegistryDataObserver = (source, data) -> { - sync(); - }; - this.unitDataObservable = new ObservableImpl<>(); - this.unitDataObserver = (source, message) -> { - try { - unitDataObservable.notifyObservers(((Unit) source), (Message) message); - } catch (ClassCastException ex) { - ExceptionPrinter.printHistory("Could not handle incoming data because type is unknown!", ex, LOGGER); - } - }; - this.serviceStateObservable = new ObservableImpl<>(); - this.serviceStateObserver = (source, data) -> { - try { - serviceStateObservable.notifyObservers((ServiceStateProvider) source, (Message) data); - } catch (ClassCastException ex) { - ExceptionPrinter.printHistory("Could not handle incoming data because type is unknown!", ex, LOGGER); - } - }; - - } catch (CouldNotPerformException ex) { - throw new InstantiationException(this, ex); - } - } - - /** - * This filter initialization is optional. - * Method sets a new filter set for the custom pool. - * If the pool is already active, then the filter is directly applied. - * If you call this method twice, only the latest filter set is used. - * - * @param unitFilter this is a unit filter that can be used to limit the number of unit to observer. No filter means every unit is observed by this pool. - * - * @throws InitializationException is throw if the initialization fails. - * @throws InterruptedException is thrown if the thread was externally interrupted. - */ - public final void init(final UnitFilter unitFilter) throws InitializationException, InterruptedException { - init(new UnitConfigFilterImpl(unitFilter)); - } - - /** - * This filter initialization is optional. - * Method sets a new filter set for the custom pool. - * If the pool is already active, then the filter is directly applied. - * If you call this method twice, only the latest filter set is used. - * - * @param filters this set of filters can be used to limit the number of unit to observer. No filter means every unit is observed by this pool. - * - * @throws InitializationException is throw if the initialization fails. - * @throws InterruptedException is thrown if the thread was externally interrupted. - */ - public final void init(final Filter... filters) throws InitializationException, InterruptedException { - init(Arrays.asList(filters)); - } - - /** - * This filter initialization is optional. - * Method sets a new filter set for the custom pool. - * If the pool is already active, then the filter is directly applied. - * If you call this method twice, only the latest filter set is used. - * - * @param filters this set of filters can be used to limit the number of unit to observer. No filter means every unit is observed by this pool. - * - * @throws InitializationException is throw if the initialization fails. - * @throws InterruptedException is thrown if the thread was externally interrupted. - */ - @Override - public void init(final Collection> filters) throws InitializationException, InterruptedException { - UNIT_REMOTE_REGISTRY_LOCK.writeLock().lockInterruptibly(); - try { - filterSet.clear(); - filterSet.addAll(filters); - if (active) { - sync(); - } - } finally { - UNIT_REMOTE_REGISTRY_LOCK.writeLock().unlock(); - } - } - - private void sync() throws InterruptedException { - // skip if registry is not ready yet. - try { - if (!Registries.getUnitRegistry().isDataAvailable()) { - return; - } - } catch (NotAvailableException e) { - return; - } - - try { - UNIT_REMOTE_REGISTRY_LOCK.writeLock().lockInterruptibly(); - try { - // todo: why not filter before diff? That would make so much things easier here. - unitConfigDiff.diffMessages(Registries.getUnitRegistry().getUnitConfigs()); - - // handle new units - unitLoop: - for (Entry> entry : unitConfigDiff.getNewMessageMap().entrySet()) { - - // apply unit filter - for (Filter filter : filterSet) { - if (filter.pass(entry.getValue().getMessage())) { - continue unitLoop; - } - } - addUnitRemote(entry.getKey()); - } - - // handle updated units - unitLoop: - for (Entry> entry : unitConfigDiff.getUpdatedMessageMap().entrySet()) { - - for (Filter filter : filterSet) { - - // remove known units which pass the filter - if (filter.pass(entry.getValue().getMessage()) && unitRemoteRegistry.contains(entry.getKey())) { - removeUnitRemote(entry.getKey()); - continue unitLoop; - } - - // we are done if unit is already known - if (unitRemoteRegistry.contains(entry.getKey())) { - continue unitLoop; - } - - // filter if required - if (filter.pass(entry.getValue().getMessage())) { - continue unitLoop; - } - } - - // unit has not been removed, is not already in the pool and is not skipped by the filters, therefore we need to add it - addUnitRemote(entry.getKey()); - } - - //handle removed units - for (Entry> entry : unitConfigDiff.getRemovedMessageMap().entrySet()) { - if (unitRemoteRegistry.contains(entry.getKey())) { - removeUnitRemote(entry.getKey()); - } - } - - // validate already registered units - unitLoop: - for (Unit unit : new ArrayList<>(unitRemoteRegistry.getEntries())) { - - // apply unit filter - for (Filter filter : filterSet) { - if (filter.pass(unit.getConfig())) { - removeUnitRemote(unit.getId()); - continue unitLoop; - } - } - } - - } finally { - UNIT_REMOTE_REGISTRY_LOCK.writeLock().unlock(); - } - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Could not sync " + this, ex, LOGGER); - } - } - - private void addUnitRemote(final String unitId) throws InterruptedException { - try { - - if (!isActive()) { - new FatalImplementationErrorException("unid remote registered but pool was never activated!", this); - } - - final UnitRemote unitRemote = Units.getUnit(unitId, false); - unitRemoteRegistry.register(unitRemote); - - for (ServiceType serviceType : unitRemote.getAvailableServiceTypes()) { - unitRemote.addServiceStateObserver(serviceType, serviceStateObserver); - } - unitRemote.addDataObserver(unitDataObserver); - } catch (CouldNotPerformException ex) { - if (!ExceptionProcessor.isCausedBySystemShutdown(ex)) { - ExceptionPrinter.printHistory("Could not add " + unitId, ex, LOGGER); - } - } - } - - private void removeUnitRemote(final String unitId) { - try { - final UnitRemote unitRemote; - try { - unitRemote = unitRemoteRegistry.get(unitId); - } catch (NotAvailableException ex) { - // unit not registered so removal not necessary. - return; - } - for (ServiceType serviceType : unitRemote.getAvailableServiceTypes()) { - unitRemote.removeServiceStateObserver(serviceType, serviceStateObserver); - } - unitRemote.removeDataObserver(unitDataObserver); - unitRemoteRegistry.remove(unitRemote); - } catch (CouldNotPerformException ex) { - if (!ExceptionProcessor.isCausedBySystemShutdown(ex)) { - ExceptionPrinter.printHistory("Could not remove " + unitId, ex, LOGGER); - } - } - } - - /** - * Method registers the given observer to all internal unit remotes to get informed about state changes. - * - * @param observer is the observer to register. - */ - public void addDataObserver(Observer, Message> observer) { - unitDataObservable.addObserver(observer); - } - - /** - * Method removes the given observer from all internal unit remotes. - * - * @param observer is the observer to remove. - */ - public void removeDataObserver(Observer, Message> observer) { - unitDataObservable.removeObserver(observer); - } - - /** - * Method registers the given observer to all internal unit remotes to get informed about state changes. - * - * @param observer is the observer to register. - */ - public void addServiceStateObserver(Observer, Message> observer) { - serviceStateObservable.addObserver(observer); - } - - /** - * Method removes the given observer from all internal unit remotes. - * - * @param observer is the observer to remove. - */ - public void removeServiceStateObserver(Observer, Message> observer) { - serviceStateObservable.removeObserver(observer); - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } - - @Override - public void activate() throws CouldNotPerformException, InterruptedException { - UNIT_REMOTE_REGISTRY_LOCK.writeLock().lockInterruptibly(); - try { - - // skip run if already active - if (isActive()) { - return; - } - - // add observer - Registries.getUnitRegistry().addDataObserver(unitRegistryDataObserver); - - active = true; - - // trigger initial sync - if (Registries.getUnitRegistry().isDataAvailable()) { - sync(); - } - } finally { - UNIT_REMOTE_REGISTRY_LOCK.writeLock().unlock(); - } - } - - @Override - public void deactivate() throws CouldNotPerformException, InterruptedException { - UNIT_REMOTE_REGISTRY_LOCK.writeLock().lockInterruptibly(); - try { - active = false; - try { - Registries.getUnitRegistry().removeDataObserver(unitRegistryDataObserver); - } catch (NotAvailableException ex) { - // if the registry is not available an observer deregistration is not required. - // This can for example be the case when the unit registry has already been terminated during the shutdown progress. - } - - // deregister all observed units - for (String unitId : new ArrayList<>(unitRemoteRegistry.getEntryMap().keySet())) { - removeUnitRemote(unitId); - } - } finally { - UNIT_REMOTE_REGISTRY_LOCK.writeLock().unlock(); - } - } - - @Override - public boolean isActive() { - return active; - } - - public List> getInternalUnitList() { - return unitRemoteRegistry.getEntries(); - } -} 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 new file mode 100644 index 0000000000..2aaa885cce --- /dev/null +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPool.kt @@ -0,0 +1,307 @@ +package org.openbase.bco.dal.remote.layer.unit + +import com.google.protobuf.Message +import org.openbase.bco.dal.lib.layer.service.ServiceStateProvider +import org.openbase.bco.dal.lib.layer.unit.Unit +import org.openbase.bco.dal.lib.layer.unit.UnitRemote +import org.openbase.bco.registry.remote.Registries +import org.openbase.bco.registry.unit.lib.filter.UnitConfigFilterImpl +import org.openbase.jul.exception.* +import org.openbase.jul.exception.ExceptionProcessor.isCausedBySystemShutdown +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.extension.protobuf.ProtobufListDiff +import org.openbase.jul.iface.Manageable +import org.openbase.jul.pattern.Filter +import org.openbase.jul.pattern.ObservableImpl +import org.openbase.jul.pattern.Observer +import org.openbase.jul.pattern.provider.DataProvider +import org.openbase.jul.storage.registry.RemoteControllerRegistry +import org.openbase.type.domotic.registry.UnitRegistryDataType +import org.openbase.type.domotic.unit.UnitConfigType +import org.openbase.type.domotic.unit.UnitFilterType +import org.slf4j.LoggerFactory +import java.util.* +import java.util.concurrent.locks.ReentrantReadWriteLock + +/*- + * #%L + * BCO DAL Remote + * %% + * 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 CustomUnitPool> : Manageable>> { + private val UNIT_REMOTE_REGISTRY_LOCK = ReentrantReadWriteLock() + private var unitRegistryDataObserver: Observer, UnitRegistryDataType.UnitRegistryData> + private var unitDataObserver: Observer, M> + private var serviceStateObserver: Observer, out Message> + private var unitRemoteRegistry: RemoteControllerRegistry + private var unitConfigDiff: ProtobufListDiff + private var filterSet: MutableSet> + private var unitDataObservable: ObservableImpl, Message> + private var serviceStateObservable: ObservableImpl, Message> + + @Volatile + private var active = false + + + init { + try { + filterSet = HashSet() + unitConfigDiff = ProtobufListDiff() + unitRemoteRegistry = RemoteControllerRegistry() + unitRegistryDataObserver = + Observer { source: DataProvider, data: UnitRegistryDataType.UnitRegistryData? -> sync() } + unitDataObservable = ObservableImpl() + unitDataObserver = Observer { source: Any, message: Any -> + try { + unitDataObservable.notifyObservers(source as Unit, message as Message) + } catch (ex: ClassCastException) { + ExceptionPrinter.printHistory("Could not handle incoming data because type is unknown!", ex, LOGGER) + } + } + serviceStateObservable = ObservableImpl() + serviceStateObserver = Observer { source: Any, data: Any -> + try { + serviceStateObservable.notifyObservers(source as ServiceStateProvider, data as Message) + } catch (ex: ClassCastException) { + ExceptionPrinter.printHistory("Could not handle incoming data because type is unknown!", ex, LOGGER) + } + } + } catch (ex: CouldNotPerformException) { + throw InstantiationException(this, ex) + } + } + + /** + * This filter initialization is optional. + * Method sets a new filter set for the custom pool. + * If the pool is already active, then the filter is directly applied. + * If you call this method twice, only the latest filter set is used. + * + * @param unitFilter this is a unit filter that can be used to limit the number of unit to observer. No filter means every unit is observed by this pool. + * + * @throws InitializationException is throw if the initialization fails. + * @throws InterruptedException is thrown if the thread was externally interrupted. + */ + @Throws(InitializationException::class, InterruptedException::class) + fun init(unitFilter: UnitFilterType.UnitFilter) { + init(UnitConfigFilterImpl(unitFilter)) + } + + /** + * This filter initialization is optional. + * Method sets a new filter set for the custom pool. + * If the pool is already active, then the filter is directly applied. + * If you call this method twice, only the latest filter set is used. + * + * @param filters this set of filters can be used to limit the number of unit to observer. No filter means every unit is observed by this pool. + * + * @throws InitializationException is throw if the initialization fails. + * @throws InterruptedException is thrown if the thread was externally interrupted. + */ + @Throws(InitializationException::class, InterruptedException::class) + fun init(vararg filters: Filter) { + init(filters.toList()) + } + + /** + * This filter initialization is optional. + * Method sets a new filter set for the custom pool. + * If the pool is already active, then the filter is directly applied. + * If you call this method twice, only the latest filter set is used. + * + * @param filters this set of filters can be used to limit the number of unit to observer. No filter means every unit is observed by this pool. + * + * @throws InitializationException is throw if the initialization fails. + * @throws InterruptedException is thrown if the thread was externally interrupted. + */ + @Throws(InitializationException::class, InterruptedException::class) + override fun init(filters: Collection>) { + UNIT_REMOTE_REGISTRY_LOCK.writeLock().lockInterruptibly() + try { + filterSet.clear() + filterSet.addAll(filters) + if (active) { + sync() + } + } finally { + UNIT_REMOTE_REGISTRY_LOCK.writeLock().unlock() + } + } + + @Throws(InterruptedException::class) + private fun sync() { + // skip if registry is not ready yet. + try { + if (!Registries.getUnitRegistry().isDataAvailable) { + return + } + } catch (e: NotAvailableException) { + return + } + try { + UNIT_REMOTE_REGISTRY_LOCK.writeLock().lockInterruptibly() + try { + // update list diff + Registries.getUnitRegistry().unitConfigs + .filter { unitConfig -> filterSet.all { it.match(unitConfig) } } + .let { unitConfigDiff.diffMessages(it) } + + // handle new units + unitConfigDiff.newMessageMap.keys.forEach { addUnitRemote(it) } + + + //handle removed units + unitConfigDiff.removedMessageMap.keys.forEach { removeUnitRemote(it) } + } finally { + UNIT_REMOTE_REGISTRY_LOCK.writeLock().unlock() + } + } catch (ex: CouldNotPerformException) { + ExceptionPrinter.printHistory("Could not sync $this", ex, LOGGER) + } + } + + @Throws(InterruptedException::class) + private fun addUnitRemote(unitId: String) { + try { + if (!isActive) { + FatalImplementationErrorException("unid remote registered but pool was never activated!", this) + } + val unitRemote: UnitRemote = Units.getUnit(unitId, false) + unitRemoteRegistry.register(unitRemote as U) + for (serviceType in unitRemote.availableServiceTypes) { + unitRemote.addServiceStateObserver(serviceType, serviceStateObserver) + } + unitRemote.addDataObserver(unitDataObserver) + } catch (ex: CouldNotPerformException) { + if (!isCausedBySystemShutdown(ex)) { + ExceptionPrinter.printHistory("Could not add $unitId", ex, LOGGER) + } + } + } + + private fun removeUnitRemote(unitId: String) { + try { + unitRemoteRegistry[unitId]?.let { unitRemote -> + unitRemote.availableServiceTypes.forEach { serviceType -> + unitRemote.removeServiceStateObserver(serviceType, serviceStateObserver) + } + unitRemote.removeDataObserver(unitDataObserver) + unitRemoteRegistry.remove(unitRemote) + } + } catch (ex: CouldNotPerformException) { + if (!isCausedBySystemShutdown(ex)) { + ExceptionPrinter.printHistory("Could not remove $unitId", ex, LOGGER) + } + } + } + + /** + * Method registers the given observer to all internal unit remotes to get informed about state changes. + * + * @param observer is the observer to register. + */ + fun addDataObserver(observer: Observer, Message>) { + unitDataObservable.addObserver(observer) + } + + /** + * Method removes the given observer from all internal unit remotes. + * + * @param observer is the observer to remove. + */ + fun removeDataObserver(observer: Observer, Message>) { + unitDataObservable.removeObserver(observer) + } + + /** + * Method registers the given observer to all internal unit remotes to get informed about state changes. + * + * @param observer is the observer to register. + */ + fun addServiceStateObserver(observer: Observer, Message>) { + serviceStateObservable.addObserver(observer) + } + + /** + * Method removes the given observer from all internal unit remotes. + * + * @param observer is the observer to remove. + */ + fun removeServiceStateObserver(observer: Observer, Message>) { + serviceStateObservable.removeObserver(observer) + } + + override fun toString(): String { + return javaClass.simpleName + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun activate() { + UNIT_REMOTE_REGISTRY_LOCK.writeLock().lockInterruptibly() + try { + + // skip run if already active + if (isActive) { + return + } + + // add observer + Registries.getUnitRegistry().addDataObserver(unitRegistryDataObserver) + active = true + + // trigger initial sync + if (Registries.getUnitRegistry().isDataAvailable) { + sync() + } + } finally { + UNIT_REMOTE_REGISTRY_LOCK.writeLock().unlock() + } + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun deactivate() { + UNIT_REMOTE_REGISTRY_LOCK.writeLock().lockInterruptibly() + try { + active = false + try { + Registries.getUnitRegistry().removeDataObserver(unitRegistryDataObserver) + } catch (ex: NotAvailableException) { + // if the registry is not available an observer deregistration is not required. + // This can for example be the case when the unit registry has already been terminated during the shutdown progress. + } + + // deregister all observed units + for (unitId in ArrayList(unitRemoteRegistry.entryMap.keys)) { + removeUnitRemote(unitId) + } + } finally { + UNIT_REMOTE_REGISTRY_LOCK.writeLock().unlock() + } + } + + override fun isActive(): Boolean = active + + val internalUnitList: List + get() = unitRemoteRegistry.entries + + companion object { + private val LOGGER = LoggerFactory.getLogger(CustomUnitPool::class.java) + } +} diff --git a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/Units.java b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/Units.java index 7d10f7026f..3d4daa921e 100644 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/Units.java +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/Units.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 * . @@ -53,7 +53,6 @@ import org.openbase.jul.storage.registry.RemoteControllerRegistry; import org.openbase.rct.Transform; import org.openbase.type.communication.ScopeType; -import org.openbase.type.communication.ScopeType.Scope; import org.openbase.type.domotic.registry.UnitRegistryDataType.UnitRegistryData; import org.openbase.type.domotic.state.EnablingStateType.EnablingState; import org.openbase.type.domotic.unit.UnitConfigType; @@ -61,6 +60,7 @@ import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -384,9 +384,9 @@ private static void resetUnitRegistryObserver() throws CouldNotPerformException * @throws InterruptedException is thrown if the current thread was * externally interrupted. */ - private static UnitRemote getUnitRemote(final String unitId) throws NotAvailableException, InterruptedException { + private static UnitRemote getUnitRemote(final String unitId) throws NotAvailableException, InterruptedException { final boolean newInstance; - final UnitRemote unitRemote; + final UnitRemote unitRemote; try { if (shutdownInitialized) { @@ -488,7 +488,7 @@ private static UnitRemote getUnitRemote(final UnitConfig unitConfig) throws N * @throws InterruptedException is thrown in case the thread is externally * interrupted. */ - private static UnitRemote waitForData(final UnitRemote unitRemote, final boolean waitForData) throws CouldNotPerformException, InterruptedException { + private static UnitRemote waitForData(final UnitRemote unitRemote, final boolean waitForData) throws CouldNotPerformException, InterruptedException { if (waitForData) { Registries.getUnitRegistry(true); unitRemote.waitForData(); @@ -510,7 +510,7 @@ private static UnitRemote waitForData(final UnitRemote unitRemote, final b * @throws InterruptedException is thrown in case the thread is externally * interrupted. */ - private static UnitRemote waitForData(final UnitRemote unitRemote, final long timeout, final TimeUnit timeUnit) throws CouldNotPerformException, InterruptedException { + private static UnitRemote waitForData(final UnitRemote unitRemote, final long timeout, final TimeUnit timeUnit) throws CouldNotPerformException, InterruptedException { final TimeoutSplitter timeoutSplitter = new TimeoutSplitter(timeout, timeUnit); Registries.getUnitRegistry(timeoutSplitter.getTime(), timeoutSplitter.getTimeUnit()); unitRemote.waitForData(timeoutSplitter.getTime(), timeoutSplitter.getTimeUnit()); @@ -847,7 +847,7 @@ public static > UR getUnitByAlias(final String alias, f * @throws InterruptedException is thrown in case the thread is externally * interrupted */ - public static UnitRemote getUnit(final String unitId, final boolean waitForData) throws NotAvailableException, InterruptedException { + public static UnitRemote getUnit(final String unitId, final boolean waitForData) throws NotAvailableException, InterruptedException { if (unitId == null) { assert false; @@ -855,7 +855,7 @@ public static UnitRemote getUnit(final String unitId, final boolean waitForDa } try { - return waitForData(getUnitRemote(unitId), waitForData); + return waitForData(((UnitRemote) getUnitRemote(unitId)), waitForData); } catch (CouldNotPerformException ex) { throw new NotAvailableException("Unit[" + unitId + "]", ex); } diff --git a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/printer/UnitStatePrinter.java b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/printer/UnitStatePrinter.java index 475451fc8a..95160df54d 100644 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/printer/UnitStatePrinter.java +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/printer/UnitStatePrinter.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 * . @@ -107,27 +107,19 @@ public Config setPrintInitialStates(boolean printInitialStates) { } public UnitStatePrinter(final PrintStream printStream, final Config config) throws InstantiationException { - try { - this.config = config; - this.outputConsumer = null; - this.printStream = printStream; - this.customUnitPool = new CustomUnitPool(); - this.unitStateObserver = (source, data) -> print((Unit) source.getServiceProvider(), source.getServiceType(), data); - } catch (CouldNotPerformException ex) { - throw new InstantiationException(this, ex); - } + this.config = config; + this.outputConsumer = null; + this.printStream = printStream; + this.customUnitPool = new CustomUnitPool(); + this.unitStateObserver = (source, data) -> print((Unit) source.getServiceProvider(), source.getServiceType(), data); } public UnitStatePrinter(final Consumer outputConsumer, final Config config) throws InstantiationException { - try { - this.config = config; - this.outputConsumer = outputConsumer; - this.printStream = null; - this.customUnitPool = new CustomUnitPool(); - this.unitStateObserver = (source, data) -> print((Unit) source.getServiceProvider(), source.getServiceType(), data); - } catch (CouldNotPerformException ex) { - throw new InstantiationException(this, ex); - } + this.config = config; + this.outputConsumer = outputConsumer; + this.printStream = null; + this.customUnitPool = new CustomUnitPool(); + this.unitStateObserver = (source, data) -> print((Unit) source.getServiceProvider(), source.getServiceType(), data); } @SafeVarargs @@ -220,11 +212,11 @@ private void print(Unit unit, ServiceType serviceType, Message serviceState) // compute related units to filter for (ActionReferenceType.ActionReference cause : responsibleAction.getActionCauseList()) { - relatedUnitIdServiceTypePair.add("['" + IdResolver.getId(cause.getServiceStateDescription().getUnitId()) + "', "+cause.getServiceStateDescription().getServiceType().name().toLowerCase()+"]"); + relatedUnitIdServiceTypePair.add("['" + IdResolver.getId(cause.getServiceStateDescription().getUnitId()) + "', " + cause.getServiceStateDescription().getServiceType().name().toLowerCase() + "]"); } for (ActionReferenceType.ActionReference impact : responsibleAction.getActionImpactList()) { - relatedUnitIdServiceTypePair.add("['" + IdResolver.getId(impact.getServiceStateDescription().getUnitId()) + "', "+impact.getServiceStateDescription().getServiceType().name().toLowerCase()+"]"); + relatedUnitIdServiceTypePair.add("['" + IdResolver.getId(impact.getServiceStateDescription().getUnitId()) + "', " + impact.getServiceStateDescription().getServiceType().name().toLowerCase() + "]"); } } 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/test/src/test/java/org/openbase/bco/dal/test/layer/unit/connection/ConnectionRemoteTest.kt b/module/dal/test/src/test/java/org/openbase/bco/dal/test/layer/unit/connection/ConnectionRemoteTest.kt index 92f0814f0c..7397ef8a6a 100644 --- a/module/dal/test/src/test/java/org/openbase/bco/dal/test/layer/unit/connection/ConnectionRemoteTest.kt +++ b/module/dal/test/src/test/java/org/openbase/bco/dal/test/layer/unit/connection/ConnectionRemoteTest.kt @@ -1,7 +1,6 @@ package org.openbase.bco.dal.test.layer.unit.connection import io.kotest.matchers.comparables.shouldBeEqualComparingTo -import org.junit.jupiter.api.RepeatedTest import org.junit.jupiter.api.Test import org.junit.jupiter.api.Timeout import org.openbase.bco.dal.lib.state.States @@ -53,7 +52,7 @@ class ConnectionRemoteTest : AbstractBCOLocationManagerTest() { .getUnitConfigByAlias(MockRegistry.ALIAS_REED_SWITCH_HEAVEN_STAIRWAY_GATE) val reedContactController = reedContactConfig - .let { deviceManagerLauncher.launchable.unitControllerRegistry[it.id] } + .let { deviceManagerLauncher.launchable!!.unitControllerRegistry[it.id] } val reedContact = reedContactConfig .let { Units.getUnit(it, true, Units.REED_CONTACT) } 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/build.gradle.kts b/module/device/openhab/build.gradle.kts index 275940eb7f..15a8359c06 100644 --- a/module/device/openhab/build.gradle.kts +++ b/module/device/openhab/build.gradle.kts @@ -14,12 +14,13 @@ application { } dependencies { - api(project(":bco.dal.control")) - api("org.glassfish.jersey.core:jersey-client:_") - api("org.glassfish.jersey.inject:jersey-hk2:_") - api("org.glassfish.jersey.media:jersey-media-sse:_") - api("org.glassfish.jersey.security:oauth2-client:_") - api("org.openhab.core.bundles:org.openhab.core.io.rest.core:_") + implementation(project(":bco.dal.control")) + implementation("jakarta.activation:jakarta.activation-api:2.1.2") + implementation("org.glassfish.jersey.core:jersey-client:_") + implementation("org.glassfish.jersey.inject:jersey-hk2:_") + implementation("org.glassfish.jersey.media:jersey-media-sse:_") + implementation("org.glassfish.jersey.security:oauth2-client:_") + implementation("org.openhab.core.bundles:org.openhab.core.io.rest.core:_") } description = "BCO Openhab Device Manager" diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestCommunicator.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestCommunicator.java index 55aaea2927..97618bfd04 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestCommunicator.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestCommunicator.java @@ -24,7 +24,9 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; +import jakarta.ws.rs.core.MediaType; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InitializationException; import org.openbase.jul.exception.InstantiationException; @@ -41,7 +43,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.ws.rs.core.MediaType; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -90,11 +91,11 @@ private OpenHABRestCommunicator() throws InstantiationException { // ========================================================================================================================================== public EnrichedThingDTO registerThing(final ThingDTO thingDTO) throws CouldNotPerformException { - return jsonToClass(jsonParser.parse(postJson(THINGS_TARGET, thingDTO)), EnrichedThingDTO.class); + return jsonToClass(JsonParser.parseString(postJson(THINGS_TARGET, thingDTO)), EnrichedThingDTO.class); } public EnrichedThingDTO updateThing(final EnrichedThingDTO enrichedThingDTO) throws CouldNotPerformException { - return jsonToClass(jsonParser.parse(putJson(THINGS_TARGET + SEPARATOR + enrichedThingDTO.UID, enrichedThingDTO)), EnrichedThingDTO.class); + return jsonToClass(JsonParser.parseString(putJson(THINGS_TARGET + SEPARATOR + enrichedThingDTO.UID, enrichedThingDTO)), EnrichedThingDTO.class); } public EnrichedThingDTO deleteThing(final EnrichedThingDTO enrichedThingDTO) throws CouldNotPerformException { @@ -102,19 +103,19 @@ public EnrichedThingDTO deleteThing(final EnrichedThingDTO enrichedThingDTO) thr } public EnrichedThingDTO deleteThing(final String thingUID) throws CouldNotPerformException { - return jsonToClass(jsonParser.parse(delete(THINGS_TARGET + SEPARATOR + thingUID)), EnrichedThingDTO.class); + return jsonToClass(JsonParser.parseString(delete(THINGS_TARGET + SEPARATOR + thingUID)), EnrichedThingDTO.class); } public EnrichedThingDTO getThing(final String thingUID) throws NotAvailableException { try { - return jsonToClass(jsonParser.parse(get(THINGS_TARGET + SEPARATOR + thingUID)), EnrichedThingDTO.class); + return jsonToClass(JsonParser.parseString(get(THINGS_TARGET + SEPARATOR + thingUID)), EnrichedThingDTO.class); } catch (CouldNotPerformException ex) { throw new NotAvailableException("Thing[" + thingUID + "]"); } } public List getThings() throws CouldNotPerformException { - return jsonElementToTypedList(jsonParser.parse(get(THINGS_TARGET)), EnrichedThingDTO.class); + return jsonElementToTypedList(JsonParser.parseString(get(THINGS_TARGET)), EnrichedThingDTO.class); } // ========================================================================================================================================== @@ -128,11 +129,11 @@ public ItemDTO registerItem(final ItemDTO itemDTO) throws CouldNotPerformExcepti } public List registerItems(final List itemDTOList) throws CouldNotPerformException { - return jsonElementToTypedList(jsonParser.parse(putJson(ITEMS_TARGET, itemDTOList)), ItemDTO.class); + return jsonElementToTypedList(JsonParser.parseString(putJson(ITEMS_TARGET, itemDTOList)), ItemDTO.class); } public ItemDTO updateItem(final ItemDTO itemDTO) throws CouldNotPerformException { - return jsonToClass(jsonParser.parse(putJson(ITEMS_TARGET + SEPARATOR + itemDTO.name, itemDTO)), ItemDTO.class); + return jsonToClass(JsonParser.parseString(putJson(ITEMS_TARGET + SEPARATOR + itemDTO.name, itemDTO)), ItemDTO.class); } public ItemDTO deleteItem(final ItemDTO itemDTO) throws CouldNotPerformException { @@ -141,16 +142,16 @@ public ItemDTO deleteItem(final ItemDTO itemDTO) throws CouldNotPerformException public ItemDTO deleteItem(final String itemName) throws CouldNotPerformException { LOGGER.warn("Delete item {}", itemName); - return jsonToClass(jsonParser.parse(delete(ITEMS_TARGET + SEPARATOR + itemName)), ItemDTO.class); + return jsonToClass(JsonParser.parseString(delete(ITEMS_TARGET + SEPARATOR + itemName)), ItemDTO.class); } public List getItems() throws CouldNotPerformException { - return jsonElementToTypedList(jsonParser.parse(get(ITEMS_TARGET)), EnrichedItemDTO.class); + return jsonElementToTypedList(JsonParser.parseString(get(ITEMS_TARGET)), EnrichedItemDTO.class); } public EnrichedItemDTO getItem(final String itemName) throws NotAvailableException { try { - return jsonToClass(jsonParser.parse(get(ITEMS_TARGET + SEPARATOR + itemName)), EnrichedItemDTO.class); + return jsonToClass(JsonParser.parseString(get(ITEMS_TARGET + SEPARATOR + itemName)), EnrichedItemDTO.class); } catch (CouldNotPerformException ex) { throw new NotAvailableException("Item with name[" + itemName + "]"); } @@ -194,7 +195,7 @@ public void deleteItemChannelLink(final String itemName, final String channelUID } public List getItemChannelLinks() throws CouldNotPerformException { - return jsonElementToTypedList(jsonParser.parse(get(LINKS_TARGET)), ItemChannelLinkDTO.class); + return jsonElementToTypedList(JsonParser.parseString(get(LINKS_TARGET)), ItemChannelLinkDTO.class); } // ========================================================================================================================================== @@ -222,7 +223,7 @@ public void approve(final String thingUID, final String label) throws CouldNotPe } public List getDiscoveryResults() throws CouldNotPerformException { - return jsonElementToTypedList(jsonParser.parse(get(INBOX_TARGET)), DiscoveryResultDTO.class); + return jsonElementToTypedList(JsonParser.parseString(get(INBOX_TARGET)), DiscoveryResultDTO.class); } // ========================================================================================================================================== diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestConnection.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestConnection.java index 34dbd5c94a..16ef2ccd5d 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestConnection.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestConnection.java @@ -23,6 +23,15 @@ */ import com.google.gson.*; +import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.sse.InboundSseEvent; +import jakarta.ws.rs.sse.SseEventSource; import org.glassfish.jersey.client.oauth2.OAuth2ClientSupport; import org.openbase.bco.device.openhab.jp.JPOpenHABURI; import org.openbase.bco.registry.remote.Registries; @@ -48,16 +57,6 @@ import org.openhab.core.types.CommandDescription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - -import javax.ws.rs.ProcessingException; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.sse.InboundSseEvent; -import javax.ws.rs.sse.SseEventSource; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -89,7 +88,6 @@ public abstract class OpenHABRestConnection implements Shutdownable { private boolean shutdownInitiated = false; - protected final JsonParser jsonParser; protected final Gson gson; private ScheduledFuture connectionTask; @@ -114,7 +112,7 @@ public boolean shouldSkipClass(Class aClass) { return false; } }).create(); - this.jsonParser = new JsonParser(); + this.restClient = ClientBuilder.newClient(); try { restClient.register(OAuth2ClientSupport.feature(getToken())); @@ -230,7 +228,7 @@ private void initSSE() { final Consumer evenConsumer = inboundSseEvent -> { // dispatch event try { - final JsonObject payload = jsonParser.parse(inboundSseEvent.readData()).getAsJsonObject(); + final JsonObject payload = JsonParser.parseString(inboundSseEvent.readData()).getAsJsonObject(); for (Entry> topicObserverEntry : topicObservableMap.entrySet()) { try { if (payload.get(TOPIC_KEY).getAsString().matches(topicObserverEntry.getKey())) { @@ -251,7 +249,7 @@ private void initSSE() { }; final Runnable reconnectHandler = () -> { - checkConnectionState(); + setConnectState(State.RECONNECTING); }; sseSource.register(evenConsumer, errorHandler, reconnectHandler); 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/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/OpenHABService.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/OpenHABService.java index 6a0e46d6f2..ed5f92553a 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/OpenHABService.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/OpenHABService.java @@ -37,9 +37,7 @@ import org.openbase.jul.exception.printer.LogLevel; import org.openbase.jul.processing.StringProcessor; import org.openbase.jul.schedule.FutureProcessor; -import org.openbase.jul.schedule.SyncObject; import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; -import org.openbase.type.domotic.binding.openhab.OpenhabCommandType; import org.openbase.type.domotic.service.ServiceConfigType.ServiceConfig; import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate; import org.openhab.core.types.Command; @@ -54,27 +52,24 @@ public abstract class OpenHABService> implements Se private final Logger logger = LoggerFactory.getLogger(getClass()); private final String itemName; private final ServiceTemplate.ServiceType serviceType; - private final ServiceConfig config; - private final Future[] repeatCommandTasks; - private final SyncObject repeatLastCommandMonitor = new SyncObject("RepeatLastCommandMonitor"); - protected OpenhabCommandType.OpenhabCommand.Builder lastCommand; public OpenHABService(final ST unit) throws InstantiationException { try { this.unit = unit; - this.repeatCommandTasks = new Future[2]; this.serviceType = detectServiceType(); - this.config = loadServiceConfig(); + + loadServiceConfig(); + this.itemName = OpenHABItemProcessor.generateItemName(unit.getConfig(), serviceType); } catch (CouldNotPerformException ex) { throw new InstantiationException(this, ex); } } - private ServiceConfig loadServiceConfig() throws CouldNotPerformException { + private void loadServiceConfig() throws CouldNotPerformException { for (final ServiceConfig serviceConfig : unit.getConfig().getServiceConfigList()) { if (serviceConfig.getServiceDescription().getServiceType().equals(serviceType)) { - return serviceConfig; + return; } } throw new CouldNotPerformException("Could not detect service config! Service[" + serviceType.name() + "] is not configured in Unit[" + ((Unit) unit).getId() + "]!"); @@ -120,74 +115,14 @@ public Future setState(final Message serviceState) { } } - return FutureProcessor.completedFuture(ServiceStateProcessor.getResponsibleAction(serviceState, () -> ActionDescription.getDefaultInstance())); + return FutureProcessor.completedFuture(ServiceStateProcessor.getResponsibleAction(serviceState, ActionDescription::getDefaultInstance)); } catch (CouldNotPerformException ex) { return FutureProcessor.canceledFuture(ActionDescription.class, ex); } } -// public Future executeCommand(final Command... commands) { -// if (itemName == null) { -// throw new NotAvailableException("itemID"); -// } -// -// try { -// for (final Command command : commands) { -// OpenHABRestCommunicator.getInstance().postCommand(itemName, command.toString()); -// } -// } catch (CouldNotPerformException ex) { -// if (ex.getCause() instanceof NotAvailableException) { -// throw new CouldNotPerformException("Thing may not be configured or openHAB not reachable", ex); -// } -// throw ex; -// } -// return FutureProcessor.completedFuture(null); -// } - @Override public ServiceProvider getServiceProvider() { return unit; } - - /** - * Method repeats the given command in 5 seconds. - * Make sure the last command is always stored into the {@code lastCommand} variable. - */ -// public void repeatLastCommand() { -// synchronized (repeatLastCommandMonitor) { -// -// // cancel still running tasks -// if (repeatCommandTasks[REPEAT_TASK_1] != null && !repeatCommandTasks[REPEAT_TASK_1].isDone()) { -// // cancel if still scheduled but do to cancel if already executing. -// repeatCommandTasks[REPEAT_TASK_1].cancel(false); -// } -// if (repeatCommandTasks[REPEAT_TASK_2] != null && !repeatCommandTasks[REPEAT_TASK_2].isDone()) { -// // cancel if still scheduled but do to cancel if already executing. -// repeatCommandTasks[REPEAT_TASK_2].cancel(false); -// } -// -// // this is just a bug workaround because the philip hues are sometimes skip events. -// // So to make sure they are controlled like expected we repeat the command twice. -// -// // init repeat 1 -// repeatCommandTasks[REPEAT_TASK_1] = GlobalScheduledExecutorService.schedule(() -> { -// try { -// executeCommand(lastCommand).get(ACTION_EXECUTION_TIMEOUT, TimeUnit.SECONDS); -// logger.info("repeat successfully command[" + lastCommand + "]"); -// } catch (Exception ex) { -// ExceptionPrinter.printHistory("Could not repeat openhab command!", ex, logger); -// } -// }, ACTION_EXECUTION_REPEAT_DELAY_1, TimeUnit.MILLISECONDS); -// -// // init repeat 2 -// repeatCommandTasks[REPEAT_TASK_2] = GlobalScheduledExecutorService.schedule(() -> { -// try { -// executeCommand(lastCommand).get(ACTION_EXECUTION_TIMEOUT, TimeUnit.SECONDS); -// logger.info("repeat successfully command[" + lastCommand + "]"); -// } catch (Exception ex) { -// ExceptionPrinter.printHistory("Could not repeat openhab command!", ex, logger); -// } -// }, ACTION_EXECUTION_REPEAT_DELAY_2, TimeUnit.MILLISECONDS); -// } -// } } diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/SynchronizationProcessor.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/SynchronizationProcessor.java index a884d979d8..6dac5b1436 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/SynchronizationProcessor.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/SynchronizationProcessor.java @@ -49,6 +49,7 @@ import org.openhab.core.io.rest.core.thing.EnrichedThingDTO; import org.openhab.core.items.dto.ItemDTO; import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.dto.AbstractThingDTO; import org.openhab.core.thing.dto.ChannelDTO; import org.openhab.core.thing.dto.ThingDTO; import org.openhab.core.thing.link.dto.ItemChannelLinkDTO; @@ -84,7 +85,7 @@ public class SynchronizationProcessor { * @return a device unit config for the thing as described above * @throws NotAvailableException if no device could be found */ - public static UnitConfig getDeviceForThing(final ThingDTO thingDTO) throws CouldNotPerformException { + public static UnitConfig getDeviceForThing(final AbstractThingDTO thingDTO) throws CouldNotPerformException { // iterate over all devices for (final UnitConfig deviceUnitConfig : Registries.getUnitRegistry().getUnitConfigsByUnitTypeFiltered(UnitType.DEVICE, false)) { // get the most global meta config @@ -135,7 +136,7 @@ public static String getUniquePrefix(final String uniqueId) { return uniquePrefix.toString(); } - public static UnitConfig getLocationForThing(final ThingDTO thingDTO) throws CouldNotPerformException, InterruptedException { + public static UnitConfig getLocationForThing(final AbstractThingDTO thingDTO) throws CouldNotPerformException, InterruptedException { if (thingDTO.location != null) { List locationConfigs = Registries.getUnitRegistry(true).getUnitConfigsByLabelAndUnitType(thingDTO.location, UnitType.LOCATION); @@ -178,7 +179,7 @@ public static DeviceClass getDeviceClassByDiscoveryResult(final DiscoveryResultD return resolveDeviceClass(discoveryResult.label, discoveryResult.thingTypeUID, properties); } - public static DeviceClass getDeviceClassForThing(final ThingDTO thingDTO) throws CouldNotPerformException { + public static DeviceClass getDeviceClassForThing(final AbstractThingDTO thingDTO) throws CouldNotPerformException { return resolveDeviceClass(thingDTO.label, thingDTO.thingTypeUID, thingDTO.properties); } @@ -193,7 +194,7 @@ public static GatewayClass getGatewayClassByDiscoveryResult(final DiscoveryResul return resolveGatewayClass(discoveryResult.label, discoveryResult.thingTypeUID, properties); } - public static GatewayClass getGatewayClassForThing(final ThingDTO thingDTO) throws CouldNotPerformException { + public static GatewayClass getGatewayClassForThing(final AbstractThingDTO thingDTO) throws CouldNotPerformException { return resolveGatewayClass(thingDTO.label, thingDTO.thingTypeUID, thingDTO.properties); } @@ -368,7 +369,7 @@ public static Map generateServiceMap(final UnitConf return serviceTypePatternMap; } - private static String getChannelUID(final UnitConfig unitConfig, final ServiceType serviceType, final ServicePattern servicePattern, final ThingDTO thingDTO) throws CouldNotPerformException { + private static String getChannelUID(final UnitConfig unitConfig, final ServiceType serviceType, final ServicePattern servicePattern, final EnrichedThingDTO thingDTO) throws CouldNotPerformException { String channelUID = ""; // todo: validate that unit host is available by using UnitConfigProcessor.isHostUnitAvailable(unitConfig) and make sure apps are handled as well. final UnitConfig deviceUnitConfig = Registries.getUnitRegistry().getUnitConfigById(unitConfig.getUnitHostId()); @@ -410,7 +411,7 @@ private static String getChannelUID(final UnitConfig unitConfig, final ServiceTy return channelUID; } - public static void registerAndValidateItems(final UnitConfig unitConfig, final ThingDTO thingDTO) throws CouldNotPerformException { + public static void registerAndValidateItems(final UnitConfig unitConfig, final EnrichedThingDTO thingDTO) throws CouldNotPerformException { final List itemChannelLinks = OpenHABRestCommunicator.getInstance().getItemChannelLinks(); for (final Entry entry : generateServiceMap(unitConfig).entrySet()) { final ServiceType serviceType = entry.getKey(); @@ -581,7 +582,7 @@ public static boolean updateUnitToThing(final EnrichedThingDTO thing, final Unit * @param thingDTO the thing to be removed. * @throws CouldNotPerformException if removing the thing fails. */ - public static void deleteThing(final ThingDTO thingDTO) throws CouldNotPerformException { + public static void deleteThing(final EnrichedThingDTO thingDTO) throws CouldNotPerformException { final List itemChannelLinks = OpenHABRestCommunicator.getInstance().getItemChannelLinks(); for (final ChannelDTO channel : thingDTO.channels) { for (final ItemChannelLinkDTO itemChannelLink : itemChannelLinks) { diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingDeviceUnitSynchronization.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingDeviceUnitSynchronization.java index 7e5e78bb0e..0866efea20 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingDeviceUnitSynchronization.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingDeviceUnitSynchronization.java @@ -44,6 +44,7 @@ import org.openbase.type.domotic.unit.device.DeviceClassType.DeviceClass; import org.openbase.type.domotic.unit.gateway.GatewayClassType.GatewayClass; import org.openhab.core.io.rest.core.thing.EnrichedThingDTO; +import org.openhab.core.thing.dto.AbstractThingDTO; import org.openhab.core.thing.dto.ThingDTO; import java.util.ArrayList; @@ -97,7 +98,7 @@ public void update(IdentifiableEnrichedThingDTO identifiableEnrichedThingDTO) th @Override public void register(IdentifiableEnrichedThingDTO identifiableEnrichedThingDTO) throws CouldNotPerformException, InterruptedException { logger.debug("Synchronize {} ...", identifiableEnrichedThingDTO.getDTO().UID); - final ThingDTO thingDTO = identifiableEnrichedThingDTO.getDTO(); + final EnrichedThingDTO thingDTO = identifiableEnrichedThingDTO.getDTO(); try { final UnitConfig deviceUnitConfig = SynchronizationProcessor.getDeviceForThing(thingDTO); @@ -111,7 +112,7 @@ public void register(IdentifiableEnrichedThingDTO identifiableEnrichedThingDTO) } } - private void registerDevice(ThingDTO thingDTO) throws CouldNotPerformException, InterruptedException { + private void registerDevice(EnrichedThingDTO thingDTO) throws CouldNotPerformException, InterruptedException { //TODO: should this entire action be rolled back if one part fails? // get device class for thing DeviceClass deviceClass; diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingUnitSynchronization.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingUnitSynchronization.java index 39a9458f81..2d0a0cb97c 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingUnitSynchronization.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingUnitSynchronization.java @@ -120,12 +120,12 @@ public boolean isSupported(IdentifiableEnrichedThingDTO identifiableEnrichedThin return identifiableEnrichedThingDTO.getId().startsWith(BCO_BINDING_ID); } - private String getUnitId(ThingDTO thingDTO) { + private String getUnitId(EnrichedThingDTO thingDTO) { String[] split = thingDTO.UID.split(":"); return split[split.length - 1]; } - private void registerAndValidateItems(final ThingDTO thingDTO) throws CouldNotPerformException { + private void registerAndValidateItems(final EnrichedThingDTO thingDTO) throws CouldNotPerformException { // save current item channel links to compute if some already exist final List itemChannelLinks = OpenHABRestCommunicator.getInstance().getItemChannelLinks(); // retrieve the unit belonging to the thing 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/launch/AbstractRegistryLauncher.java b/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/launch/AbstractRegistryLauncher.java index 2661b0bfe8..4994c437d5 100644 --- a/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/launch/AbstractRegistryLauncher.java +++ b/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/launch/AbstractRegistryLauncher.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 * . @@ -29,11 +29,12 @@ /** * @param + * * @author Divine Threepwood */ -public abstract class AbstractRegistryLauncher extends AbstractLauncher { +public abstract class AbstractRegistryLauncher> extends AbstractLauncher { - public AbstractRegistryLauncher(Class applicationClass, Class launchableClass) throws InstantiationException { + public AbstractRegistryLauncher(Class applicationClass, Class launchableClass) throws InstantiationException { super(applicationClass, launchableClass); } 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..431ec3e50f 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; @@ -47,6 +50,7 @@ import org.openbase.jul.storage.registry.RegistryRemote; import org.openbase.type.domotic.activity.ActivityConfigType.ActivityConfig; import org.openbase.type.domotic.activity.ActivityTemplateType.ActivityTemplate; +import org.openbase.type.domotic.communication.UserMessageType.UserMessage; import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate; import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate; @@ -80,7 +84,8 @@ public class Registries { UnitTemplate.getDefaultInstance(), ServiceTemplate.getDefaultInstance(), ActivityTemplate.getDefaultInstance(), - ActivityConfig.getDefaultInstance() + ActivityConfig.getDefaultInstance(), + UserMessage.getDefaultInstance() }; /** @@ -93,12 +98,13 @@ 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)); registryList.add(getUnitRegistry(waitForData)); + registryList.add(getMessageRegistry(waitForData)); return registryList; } @@ -109,7 +115,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 +190,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 +237,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 +332,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 +366,7 @@ public static void shutdown() { CachedActivityRegistryRemote.shutdown(); CachedClassRegistryRemote.shutdown(); CachedTemplateRegistryRemote.shutdown(); + CachedMessageRegistryRemote.shutdown(); } public static void prepare() throws CouldNotPerformException { @@ -322,15 +374,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 +402,7 @@ public static void waitForData() throws CouldNotPerformException, InterruptedExc CachedClassRegistryRemote.waitForData(); CachedActivityRegistryRemote.waitForData(); CachedUnitRegistryRemote.waitForData(); + CachedMessageRegistryRemote.waitForData(); } /** @@ -361,6 +416,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 +424,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 +443,7 @@ public static void reinitialize() throws CouldNotPerformException, InterruptedEx CachedClassRegistryRemote.reinitialize(); CachedActivityRegistryRemote.reinitialize(); CachedUnitRegistryRemote.reinitialize(); + CachedMessageRegistryRemote.reinitialize(); } /** @@ -401,6 +459,7 @@ public static void waitUntilReady() throws InterruptedException, CouldNotPerform CachedClassRegistryRemote.waitUntilReady(); CachedActivityRegistryRemote.waitUntilReady(); CachedUnitRegistryRemote.waitUntilReady(); + CachedMessageRegistryRemote.waitUntilReady(); } /** @@ -619,7 +678,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 +692,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 +715,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 5b3617b12c..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) { @@ -207,8 +207,11 @@ public void activate() throws InterruptedException, CouldNotPerformException { @Override public void deactivate() throws InterruptedException, CouldNotPerformException { this.removeDataObserver(clearUnitConfigsByTypeObserver); - CachedTemplateRegistryRemote.getRegistry().removeDataObserver(clearUnitConfigsByTypeObserver); - + try { + CachedTemplateRegistryRemote.getRegistry().removeDataObserver(clearUnitConfigsByTypeObserver); + } catch (NotAvailableException e) { + // just continue + } super.deactivate(); } @@ -429,6 +432,7 @@ protected void registerDependencies() throws CouldNotPerformException { connectionUnitConfigRegistry.registerDependency(locationUnitConfigRegistry); + agentUnitConfigRegistry.registerDependency(userUnitConfigRegistry); agentUnitConfigRegistry.registerDependency(locationUnitConfigRegistry); agentUnitConfigRegistry.registerDependency(CachedClassRegistryRemote.getRegistry().getAgentClassRemoteRegistry(false)); @@ -436,6 +440,7 @@ protected void registerDependencies() throws CouldNotPerformException { appUnitConfigRegistry.registerDependency(CachedClassRegistryRemote.getRegistry().getAppClassRemoteRegistry(false)); appUnitConfigRegistry.registerDependency(locationUnitConfigRegistry); + appUnitConfigRegistry.registerDependency(userUnitConfigRegistry); } @Override @@ -572,7 +577,9 @@ public UnitConfig getUnitConfigById(final String unitConfigId) throws NotAvailab * {@inheritDoc} * * @param unitAlias {@inheritDoc} + * * @return {@inheritDoc} + * * @throws NotAvailableException {@inheritDoc} */ @Override @@ -594,7 +601,9 @@ public UnitConfig getUnitConfigByAlias(final String unitAlias) throws NotAvailab * * @param unitAlias {@inheritDoc} * @param unitType {@inheritDoc} + * * @return {@inheritDoc} + * * @throws NotAvailableException {@inheritDoc} */ @Override @@ -674,7 +683,9 @@ public Future removeUnitConfigAuthenticated(final Authentica * {@inheritDoc} * * @param filterDisabledUnits {@inheritDoc} + * * @return {@inheritDoc} + * * @throws CouldNotPerformException {@inheritDoc} * @throws NotAvailableException {@inheritDoc} */ @@ -698,6 +709,7 @@ public List getUnitConfigsFiltered(boolean filterDisabledUnits) thro * {@inheritDoc} * * @return {@inheritDoc} + * * @throws CouldNotPerformException {@inheritDoc} */ @Override @@ -709,6 +721,7 @@ public List getDalUnitConfigs() throws CouldNotPerformException { * {@inheritDoc} * * @return {@inheritDoc} + * * @throws CouldNotPerformException {@inheritDoc} */ @Override @@ -759,7 +772,9 @@ public Boolean isUnitGroupConfigRegistryConsistent() { * {@inheritDoc} * * @param serviceType + * * @return + * * @throws CouldNotPerformException */ @Override @@ -780,7 +795,9 @@ public List getServiceConfigsByServiceType(final ServiceType serv * * @param unitType * @param serviceTypes + * * @return + * * @throws CouldNotPerformException */ @Override @@ -1048,6 +1065,7 @@ protected UnitRegistryData filterDataForUser(final UnitRegistryData.Builder data * {@inheritDoc} * * @param authorizationToken {@inheritDoc} + * * @return {@inheritDoc} */ @Override @@ -1066,6 +1084,7 @@ public Future requestAuthorizationToken(final AuthorizationToken authori * {@inheritDoc} * * @param authenticatedValue {@inheritDoc} + * * @return {@inheritDoc} */ @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/AbstractUnitTransformationRegistryPlugin.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/plugin/AbstractUnitTransformationRegistryPlugin.java index d0fa01eb5c..9f6d9e4f64 100644 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/plugin/AbstractUnitTransformationRegistryPlugin.java +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/plugin/AbstractUnitTransformationRegistryPlugin.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 * . @@ -25,18 +25,13 @@ import org.openbase.jps.core.JPService; import org.openbase.jul.exception.*; import org.openbase.jul.extension.protobuf.IdentifiableMessage; -import org.openbase.jul.iface.Transformer; -import org.openbase.jul.schedule.GlobalCachedExecutorService; import org.openbase.jul.storage.registry.ProtoBufRegistry; import org.openbase.jul.storage.registry.plugin.ProtobufRegistryPluginAdapter; import org.openbase.rct.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig.Builder; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public abstract class AbstractUnitTransformationRegistryPlugin extends ProtobufRegistryPluginAdapter { @@ -70,7 +65,7 @@ protected void verifyPublication(final Transform transformation, String targetFr // wait until transformation was published try { int maxChecks = 10; - Exception exception = new CouldNotPerformException("This should not happen"); + Exception exception = new CouldNotPerformException("Update not advertised."); for (int i = 0; i < maxChecks; i++) { try { // check if transformation was published 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..4a9221328e --- /dev/null +++ b/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt @@ -0,0 +1,430 @@ +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 { + var exceptionStack: ExceptionStack? = null + return try { + // validate sender + canDo( + authenticationBaseData, + tryOrNull { unitRegistry.getUnitConfigById(userMessage.senderId) }, + permissionType, + unitRegistry, + null, + null + ) + } catch (ex: CouldNotPerformException) { + exceptionStack = MultiException.push(AuthorizationWithTokenHelper::class.java, ex, exceptionStack) + + // validate receiver if sender validation failed. + try { + canDo( + authenticationBaseData, + tryOrNull { 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 + } + } + } + } ?: run { + + MultiException.checkAndThrow({ "Permission denied!" }, exceptionStack) + + 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 + } + } + + 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 + } + + if (unitConfig != null) { + + // 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()) + } + + // 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.isNotEmpty()) { + 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/remote/src/main/java/org/openbase/bco/registry/unit/remote/UnitRegistryRemote.java b/module/registry/unit-registry/remote/src/main/java/org/openbase/bco/registry/unit/remote/UnitRegistryRemote.java index 50f3395e9b..5ce54eb604 100644 --- a/module/registry/unit-registry/remote/src/main/java/org/openbase/bco/registry/unit/remote/UnitRegistryRemote.java +++ b/module/registry/unit-registry/remote/src/main/java/org/openbase/bco/registry/unit/remote/UnitRegistryRemote.java @@ -164,7 +164,11 @@ public void activate() throws InterruptedException, CouldNotPerformException { public void deactivate() throws InterruptedException, CouldNotPerformException { unitConfigRemoteRegistry.removeDataObserver(aliasMapUpdateObserver); unitConfigRemoteRegistry.removeDataObserver(clearUnitConfigsByTypeObserver); - CachedUnitRegistryRemote.getRegistry().removeDataObserver(clearUnitConfigsByTypeObserver); + try { + CachedUnitRegistryRemote.getRegistry().removeDataObserver(clearUnitConfigsByTypeObserver); + } catch (NotAvailableException e) { + // just continue + } super.deactivate(); } 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/settings.gradle.kts b/settings.gradle.kts index b120ccbdb4..1f08013f17 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,15 @@ rootProject.name = "bco" +pluginManagement { + plugins { + id("de.fayard.refreshVersions") version "0.51.0" + } +} + +plugins { + id("de.fayard.refreshVersions") +} + include("authentication") include("registry") include("dal") @@ -111,13 +121,3 @@ project(":bco.device.openhab").projectDir = file("module/device/openhab") // includeBuild("lib/type") // not supported yet since its not a gradle project includeBuild("lib/jul") - -pluginManagement { - plugins { - id("de.fayard.refreshVersions") version "0.40.2" - } -} - -plugins { - id("de.fayard.refreshVersions") -} diff --git a/versions.properties b/versions.properties index eb13ab729b..34b241fe1c 100644 --- a/versions.properties +++ b/versions.properties @@ -1,116 +1,35 @@ #### Dependencies and Plugin versions with their available updates. -#### Generated by `./gradlew refreshVersions` version 0.40.2 +#### Generated by `./gradlew refreshVersions` version 0.51.0 #### #### Don't manually edit or split the comments that start with four hashtags (####), #### they will be overwritten by refreshVersions. #### #### suppress inspection "SpellCheckingInspection" for whole file #### suppress inspection "UnusedProperty" for whole file - version.com.adarshr..gradle-test-logger-plugin=3.2.0 - -version.com.graphql-java-kickstart..graphql-spring-boot-starter=14.0.0 - -version.io.quarkus..quarkus-junit4-mock=2.9.1.Final - +version.com.graphql-java-kickstart..graphql-spring-boot-starter=15.0.0 +version.io.quarkus..quarkus-junit4-mock=3.2.3.Final ## unused -version.junit.jupiter=5.9.2 - -version.kotest=5.5.5 -## # available=5.6.0.1123-SNAPSHOT -## # available=5.6.0.1125-SNAPSHOT -## # available=5.6.0.1126-SNAPSHOT -## # available=5.6.0.1127-SNAPSHOT -## # available=5.6.0.1128-SNAPSHOT -## # available=5.6.0.1129-SNAPSHOT -## # available=5.6.0.1131-SNAPSHOT - -version.kotlinx.coroutines=1.6.4 -## # available=1.7.0-Beta - -version.mockk=1.13.4 - -version.org.jmdns..jmdns=3.5.7 - -version.org.glassfish.jersey.security..oauth2-client=2.31 -## # available=2.32 -## # available=2.33 -## # available=2.34 -## # available=2.35 -## # available=3.0.0-M1 -## # available=3.0.0-M6 -## # available=3.0.0-RC2 -## # available=3.0.0 -## # available=3.0.1 -## # available=3.0.2 -## # available=3.0.3 - -version.org.glassfish.jersey.media..jersey-media-sse=2.31 -## # available=2.32 -## # available=2.33 -## # available=2.34 -## # available=2.35 -## # available=3.0.0-M1 -## # available=3.0.0-M6 -## # available=3.0.0-RC2 -## # available=3.0.0 -## # available=3.0.1 -## # available=3.0.2 -## # available=3.0.3 - -version.org.glassfish.jersey.inject..jersey-hk2=2.31 -## # available=2.32 -## # available=2.33 -## # available=2.34 -## # available=2.35 -## # available=3.0.0-M1 -## # available=3.0.0-M6 -## # available=3.0.0-RC2 -## # available=3.0.0 -## # available=3.0.1 -## # available=3.0.2 -## # available=3.0.3 - -version.org.glassfish.jersey.core..jersey-client=2.31 -## # available=2.32 -## # available=2.33 -## # available=2.34 -## # available=2.35 -## # available=3.0.0-M1 -## # available=3.0.0-M6 -## # available=3.0.0-RC2 -## # available=3.0.0 -## # available=3.0.1 -## # available=3.0.2 -## # available=3.0.3 - +version.junit.jupiter=5.10.0 +version.kotest=5.6.2 +version.kotlinx.coroutines=1.7.3 +version.mockk=1.13.5 +version.org.glassfish.jersey.security..oauth2-client=3.1.3 +version.org.glassfish.jersey.media..jersey-media-sse=3.1.3 +version.org.glassfish.jersey.inject..jersey-hk2=3.1.3 +version.org.glassfish.jersey.core..jersey-client=3.1.3 version.org.apache.commons..commons-math3=3.6.1 - version.net.javacrumbs.future-converter..future-converter-java8-guava=1.2.0 - -version.io.socket..socket.io-client=1.0.0 -## # available=1.0.1 -## # available=2.0.0 -## # available=2.0.1 - +version.io.socket..socket.io-client=2.0.1 version.io.reactivex.rxjava2..rxjava=2.2.21 - -version.commons-collections..commons-collections=3.2.2 -## # available=20030418.083655 -## # available=20031027.000000 -## # available=20040102.233541 -## # available=20040616 - +version.commons-collections..commons-collections4=4.4 ## unused version.com.influxdb..influxdb-client-java=[5.0,5.1-alpha) - version.com.google.inject.extensions..guice-multibindings=4.2.3 - version.com.google.inject..guice=5.0.1 ## # available=5.0.2-SNAPSHOT ## # available=5.1.0 ## # available=5.1.1-SNAPSHOT - version.com.google.guava..guava=28.0-jre ## # available=28.1-android ## # available=28.1-jre @@ -128,102 +47,72 @@ 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.testcontainers..junit-jupiter=1.17.2 - -version.org.testcontainers..testcontainers=1.17.1 - -version.org.springframework.boot..spring-boot-starter-webflux=2.6.3 - -version.org.springframework.boot..spring-boot-starter-jetty=2.6.3 - -version.org.springframework.boot..spring-boot-starter-websocket=2.6.3 - -version.org.springframework..spring-webmvc=5.3.15 - -version.org.openhab.core.bundles..org.openhab.core.io.rest.core=3.1.0 -## # available=3.1.1 -## # available=3.2.0 - -plugin.org.springframework.boot=2.6.2 -## # available=2.6.3 - -plugin.io.spring.dependency-management=1.0.11.RELEASE - -version.kotlin=1.7.0 - -version.org.openbase..jul.communication.controller=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 +version.org.springframework.boot..spring-boot-starter-jetty=3.1.2 +version.org.springframework.boot..spring-boot-starter-websocket=3.1.2 +version.org.springframework..spring-webmvc=6.0.11 +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.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 - version.rxjava2.rxjava=2.2.21