Search in sources :

Example 1 with AgentService

use of org.openremote.manager.agent.AgentService in project openremote by openremote.

the class Main method main.

public static void main(String[] args) throws Exception {
    List<ContainerService> services = new ArrayList<ContainerService>() {

        {
            addAll(Arrays.asList(new TimerService(), new ManagerExecutorService(), new I18NService(), new ManagerPersistenceService(), new MessageBrokerSetupService(), new ManagerIdentityService(), new SetupService(), new ClientEventService(), new RulesetStorageService(), new RulesService(), new AssetStorageService(), new AssetDatapointService(), new AssetAttributeLinkingService(), new AssetProcessingService(), new MessageBrokerService()));
            ServiceLoader.load(Protocol.class).forEach(this::add);
            addAll(Arrays.asList(new AgentService(), new SimulatorService(), new MapService(), new NotificationService(), new ConsoleAppService(), new ManagerWebService()));
        }
    };
    new Container(services).startBackground();
}
Also used : MessageBrokerSetupService(org.openremote.container.message.MessageBrokerSetupService) AssetStorageService(org.openremote.manager.asset.AssetStorageService) ConsoleAppService(org.openremote.manager.apps.ConsoleAppService) ArrayList(java.util.ArrayList) AssetProcessingService(org.openremote.manager.asset.AssetProcessingService) TimerService(org.openremote.container.timer.TimerService) ManagerIdentityService(org.openremote.manager.security.ManagerIdentityService) AssetDatapointService(org.openremote.manager.datapoint.AssetDatapointService) Container(org.openremote.container.Container) AgentService(org.openremote.manager.agent.AgentService) RulesService(org.openremote.manager.rules.RulesService) ManagerWebService(org.openremote.manager.web.ManagerWebService) SimulatorService(org.openremote.manager.simulator.SimulatorService) ClientEventService(org.openremote.manager.event.ClientEventService) Protocol(org.openremote.agent.protocol.Protocol) MapService(org.openremote.manager.map.MapService) RulesetStorageService(org.openremote.manager.rules.RulesetStorageService) I18NService(org.openremote.manager.i18n.I18NService) ManagerPersistenceService(org.openremote.manager.persistence.ManagerPersistenceService) NotificationService(org.openremote.manager.notification.NotificationService) AssetAttributeLinkingService(org.openremote.manager.asset.AssetAttributeLinkingService) ManagerExecutorService(org.openremote.manager.concurrent.ManagerExecutorService) MessageBrokerSetupService(org.openremote.container.message.MessageBrokerSetupService) SetupService(org.openremote.manager.setup.SetupService) ContainerService(org.openremote.container.ContainerService) MessageBrokerService(org.openremote.container.message.MessageBrokerService)

Example 2 with AgentService

use of org.openremote.manager.agent.AgentService in project openremote by openremote.

the class AssetProcessingService method init.

@Override
public void init(Container container) throws Exception {
    timerService = container.getService(TimerService.class);
    identityService = container.getService(ManagerIdentityService.class);
    persistenceService = container.getService(PersistenceService.class);
    rulesService = container.getService(RulesService.class);
    agentService = container.getService(AgentService.class);
    assetStorageService = container.getService(AssetStorageService.class);
    assetDatapointService = container.getService(AssetDatapointService.class);
    assetAttributeLinkingService = container.getService(AssetAttributeLinkingService.class);
    messageBrokerService = container.getService(MessageBrokerService.class);
    clientEventService = container.getService(ClientEventService.class);
    clientEventService.addSubscriptionAuthorizer((auth, subscription) -> {
        if (!subscription.isEventType(AttributeEvent.class)) {
            return false;
        }
        // Always must have a filter, as you can't subscribe to ALL asset attribute events
        if (subscription.getFilter() != null && subscription.getFilter() instanceof AttributeEvent.EntityIdFilter) {
            AttributeEvent.EntityIdFilter filter = (AttributeEvent.EntityIdFilter) subscription.getFilter();
            // Superuser can get attribute events for any asset
            if (auth.isSuperUser())
                return true;
            // Regular user must have role
            if (!auth.hasResourceRole(ClientRole.READ_ASSETS.getValue(), Constants.KEYCLOAK_CLIENT_ID)) {
                return false;
            }
            boolean isRestrictedUser = identityService.getIdentityProvider().isRestrictedUser(auth.getUserId());
            // Client can subscribe to several assets
            for (String assetId : filter.getEntityId()) {
                Asset asset = assetStorageService.find(assetId);
                // If the asset doesn't exist, subscription must fail
                if (asset == null)
                    return false;
                if (isRestrictedUser) {
                    // Restricted users can only get attribute events for their linked assets
                    if (!assetStorageService.isUserAsset(auth.getUserId(), assetId))
                        return false;
                // TODO Restricted clients should only receive events for RESTRICTED_READ attributes!
                } else {
                    // Regular users can only get attribute events for assets in their realm
                    if (!asset.getTenantRealm().equals(auth.getAuthenticatedRealm()))
                        return false;
                }
            }
            return true;
        }
        return false;
    });
    processors.add(agentService);
    processors.add(rulesService);
    processors.add(assetDatapointService);
    processors.add(assetAttributeLinkingService);
    container.getService(MessageBrokerSetupService.class).getContext().addRoutes(this);
}
Also used : TimerService(org.openremote.container.timer.TimerService) AttributeEvent(org.openremote.model.attribute.AttributeEvent) ManagerIdentityService(org.openremote.manager.security.ManagerIdentityService) PersistenceService(org.openremote.container.persistence.PersistenceService) AssetDatapointService(org.openremote.manager.datapoint.AssetDatapointService) AgentService(org.openremote.manager.agent.AgentService) RulesService(org.openremote.manager.rules.RulesService) Asset(org.openremote.model.asset.Asset) ClientEventService(org.openremote.manager.event.ClientEventService) MessageBrokerService(org.openremote.container.message.MessageBrokerService)

Example 3 with AgentService

use of org.openremote.manager.agent.AgentService in project openremote by openremote.

the class AssetProcessingService method configure.

@Override
public void configure() throws Exception {
    // A client wants to write attribute state through event bus
    from(CLIENT_EVENT_TOPIC).routeId("FromClientUpdates").filter(body().isInstanceOf(AttributeEvent.class)).setHeader(HEADER_SOURCE, () -> CLIENT).to(ASSET_QUEUE);
    // Process attribute events
    /* TODO This message consumer should be transactionally consistent with the database, this is currently not the case

         Our "if I have not processed this message before" duplicate detection:

          - discard events with source time greater than server processing time (future events)
          - discard events with source time less than last applied/stored event source time
          - allow the rest (also events with same source time, order of application undefined)

         Possible improvements moving towards at-least-once:

         - Make AssetUpdateProcessor transactional with a two-phase commit API
         - Replace at-most-once ClientEventService with at-least-once capable, embeddable message broker/protocol
         - See pseudocode here: http://activemq.apache.org/should-i-use-xa.html
         - Do we want JMS/AMQP/WSS or SOME_API/MQTT/WSS? ActiveMQ or Moquette?
        */
    from(ASSET_QUEUE).routeId("AssetQueueProcessor").filter(body().isInstanceOf(AttributeEvent.class)).doTry().process(exchange -> withLock(getClass().getSimpleName() + "::processFromAssetQueue", () -> {
        AttributeEvent event = exchange.getIn().getBody(AttributeEvent.class);
        LOG.finest("Processing: " + event);
        if (event.getEntityId() == null || event.getEntityId().isEmpty())
            return;
        if (event.getAttributeName() == null || event.getAttributeName().isEmpty())
            return;
        Source source = exchange.getIn().getHeader(HEADER_SOURCE, () -> null, Source.class);
        if (source == null) {
            throw new AssetProcessingException(MISSING_SOURCE);
        }
        // Process the asset update in a database transaction, this ensures that processors
        // will see consistent database state and we only commit if no processor failed. This
        // still won't make this procedure consistent with the message queue from which we consume!
        persistenceService.doTransaction(em -> {
            ServerAsset asset = assetStorageService.find(em, event.getEntityId(), true);
            if (asset == null)
                throw new AssetProcessingException(ASSET_NOT_FOUND);
            AssetAttribute oldAttribute = asset.getAttribute(event.getAttributeName()).orElse(null);
            if (oldAttribute == null)
                throw new AssetProcessingException(ATTRIBUTE_NOT_FOUND);
            // Agent attributes can't be updated with events
            if (asset.getWellKnownType() == AssetType.AGENT) {
                throw new AssetProcessingException(ILLEGAL_AGENT_UPDATE);
            }
            // For executable attributes, non-sensor sources can set a writable attribute execute status
            if (oldAttribute.isExecutable() && source != SENSOR) {
                Optional<AttributeExecuteStatus> status = event.getValue().flatMap(Values::getString).flatMap(AttributeExecuteStatus::fromString);
                if (status.isPresent() && !status.get().isWrite()) {
                    throw new AssetProcessingException(INVALID_ATTRIBUTE_EXECUTE_STATUS);
                }
            }
            switch(source) {
                case CLIENT:
                    AuthContext authContext = exchange.getIn().getHeader(Constants.AUTH_CONTEXT, AuthContext.class);
                    if (authContext == null) {
                        throw new AssetProcessingException(NO_AUTH_CONTEXT);
                    }
                    // Check realm, must be accessible
                    if (!identityService.getIdentityProvider().isTenantActiveAndAccessible(authContext, asset)) {
                        throw new AssetProcessingException(INSUFFICIENT_ACCESS);
                    }
                    // Check read-only
                    if (oldAttribute.isReadOnly() && !authContext.isSuperUser()) {
                        throw new AssetProcessingException(INSUFFICIENT_ACCESS);
                    }
                    // Regular user must have write assets role
                    if (!authContext.hasResourceRoleOrIsSuperUser(ClientRole.WRITE_ASSETS.getValue(), Constants.KEYCLOAK_CLIENT_ID)) {
                        throw new AssetProcessingException(INSUFFICIENT_ACCESS);
                    }
                    // Check restricted user
                    if (identityService.getIdentityProvider().isRestrictedUser(authContext.getUserId())) {
                        // Must be asset linked to user
                        if (!assetStorageService.isUserAsset(authContext.getUserId(), event.getEntityId())) {
                            throw new AssetProcessingException(INSUFFICIENT_ACCESS);
                        }
                        // Must be writable by restricted client
                        if (!oldAttribute.isAccessRestrictedWrite()) {
                            throw new AssetProcessingException(INSUFFICIENT_ACCESS);
                        }
                    }
                    break;
                case SENSOR:
                    Optional<AssetAttribute> protocolConfiguration = getAgentLink(oldAttribute).flatMap(agentService::getProtocolConfiguration);
                    // Sensor event must be for an attribute linked to a protocol configuration
                    if (!protocolConfiguration.isPresent()) {
                        throw new AssetProcessingException(INVALID_AGENT_LINK);
                    }
                    break;
            }
            // Either use the timestamp of the event or set event time to processing time
            long processingTime = timerService.getCurrentTimeMillis();
            long eventTime = event.getTimestamp() > 0 ? event.getTimestamp() : processingTime;
            // the attribute until after that time (maybe that is desirable behaviour)
            if (eventTime - processingTime > 0) {
                // TODO: Decide how to handle update events in the future - ignore or change timestamp
                throw new AssetProcessingException(EVENT_IN_FUTURE, "current time: " + new Date(processingTime) + "/" + processingTime + ", event time: " + new Date(eventTime) + "/" + eventTime);
            }
            // Check the last update timestamp of the attribute, ignoring any event that is older than last update
            // TODO This means we drop out-of-sequence events but accept events with the same source timestamp
            // TODO Several attribute events can occur in the same millisecond, then order of application is undefined
            oldAttribute.getValueTimestamp().filter(t -> t >= 0 && eventTime < t).ifPresent(lastStateTime -> {
                throw new AssetProcessingException(EVENT_OUTDATED, "last asset state time: " + new Date(lastStateTime) + "/" + lastStateTime + ", event time: " + new Date(eventTime) + "/" + eventTime);
            });
            // Create a copy of the attribute and set the new value and timestamp
            AssetAttribute updatedAttribute = oldAttribute.deepCopy();
            updatedAttribute.setValue(event.getValue().orElse(null), eventTime);
            // Validate constraints of attribute
            List<ValidationFailure> validationFailures = updatedAttribute.getValidationFailures();
            if (!validationFailures.isEmpty()) {
                throw new AssetProcessingException(ATTRIBUTE_VALIDATION_FAILURE, validationFailures.toString());
            }
            // Push through all processors
            boolean consumedCompletely = processAssetUpdate(em, asset, updatedAttribute, source);
            // Publish a new event for clients if no processor consumed the update completely
            if (!consumedCompletely) {
                publishClientEvent(asset, updatedAttribute);
            }
        });
    })).endDoTry().doCatch(AssetProcessingException.class).process(handleAssetProcessingException(LOG));
}
Also used : ClientRole(org.openremote.model.security.ClientRole) AuthContext(org.openremote.container.security.AuthContext) AssetDatapointService(org.openremote.manager.datapoint.AssetDatapointService) Date(java.util.Date) CLIENT_EVENT_TOPIC(org.openremote.manager.event.ClientEventService.CLIENT_EVENT_TOPIC) ValidationFailure(org.openremote.model.ValidationFailure) Exchange(org.apache.camel.Exchange) ArrayList(java.util.ArrayList) Level(java.util.logging.Level) Processor(org.apache.camel.Processor) Container(org.openremote.container.Container) ContainerService(org.openremote.container.ContainerService) RulesService(org.openremote.manager.rules.RulesService) AttributeEvent(org.openremote.model.attribute.AttributeEvent) PersistenceService(org.openremote.container.persistence.PersistenceService) AgentService(org.openremote.manager.agent.AgentService) AgentLink.getAgentLink(org.openremote.model.asset.agent.AgentLink.getAgentLink) MessageBrokerService(org.openremote.container.message.MessageBrokerService) ManagerIdentityService(org.openremote.manager.security.ManagerIdentityService) Asset(org.openremote.model.asset.Asset) AssetType(org.openremote.model.asset.AssetType) EntityManager(javax.persistence.EntityManager) Constants(org.openremote.model.Constants) Logger(java.util.logging.Logger) MessageBrokerSetupService(org.openremote.container.message.MessageBrokerSetupService) Collectors(java.util.stream.Collectors) Reason(org.openremote.manager.asset.AssetProcessingException.Reason) AssetResource(org.openremote.model.asset.AssetResource) HEADER_SOURCE(org.openremote.model.attribute.AttributeEvent.HEADER_SOURCE) Value(org.openremote.model.value.Value) ClientEventService(org.openremote.manager.event.ClientEventService) List(java.util.List) RouteBuilder(org.apache.camel.builder.RouteBuilder) TimerService(org.openremote.container.timer.TimerService) Optional(java.util.Optional) Source(org.openremote.model.attribute.AttributeEvent.Source) Values(org.openremote.model.value.Values) AssetAttribute(org.openremote.model.asset.AssetAttribute) Protocol(org.openremote.agent.protocol.Protocol) AttributeExecuteStatus(org.openremote.model.attribute.AttributeExecuteStatus) GlobalLock.withLock(org.openremote.container.concurrent.GlobalLock.withLock) AuthContext(org.openremote.container.security.AuthContext) AttributeEvent(org.openremote.model.attribute.AttributeEvent) Source(org.openremote.model.attribute.AttributeEvent.Source) Date(java.util.Date) ValidationFailure(org.openremote.model.ValidationFailure) AttributeExecuteStatus(org.openremote.model.attribute.AttributeExecuteStatus) AssetAttribute(org.openremote.model.asset.AssetAttribute)

Aggregations

MessageBrokerService (org.openremote.container.message.MessageBrokerService)3 TimerService (org.openremote.container.timer.TimerService)3 AgentService (org.openremote.manager.agent.AgentService)3 AssetDatapointService (org.openremote.manager.datapoint.AssetDatapointService)3 ClientEventService (org.openremote.manager.event.ClientEventService)3 RulesService (org.openremote.manager.rules.RulesService)3 ManagerIdentityService (org.openremote.manager.security.ManagerIdentityService)3 ArrayList (java.util.ArrayList)2 Protocol (org.openremote.agent.protocol.Protocol)2 Container (org.openremote.container.Container)2 ContainerService (org.openremote.container.ContainerService)2 MessageBrokerSetupService (org.openremote.container.message.MessageBrokerSetupService)2 PersistenceService (org.openremote.container.persistence.PersistenceService)2 Asset (org.openremote.model.asset.Asset)2 AttributeEvent (org.openremote.model.attribute.AttributeEvent)2 Date (java.util.Date)1 List (java.util.List)1 Optional (java.util.Optional)1 Level (java.util.logging.Level)1 Logger (java.util.logging.Logger)1