Search in sources :

Example 6 with ValidationFailure

use of org.openremote.model.ValidationFailure in project openremote by openremote.

the class AttributeLinkEditor method updateAttributeLink.

protected void updateAttributeLink() {
    // Validate attributeRef
    if (attributeRef == null) {
        onValueModified.accept(null);
        return;
    }
    // Validate converter
    // Check all meta items have unique non empty names if not flag as meta item failure
    List<String> converterKeys = new ArrayList<>(converterAttribute.getMeta().size());
    AttributeValidationResult validationResult = new AttributeValidationResult(parentView.getAttribute().getName().orElse(""));
    IntStream.range(0, converterAttribute.getMeta().size()).forEach(i -> {
        MetaItem metaItem = converterAttribute.getMeta().get(i);
        List<ValidationFailure> failures = metaItem.getValidationFailures();
        if (failures != null) {
            failures.forEach(failure -> validationResult.addMetaFailure(i, failure));
        }
        if (converterKeys.contains(metaItem.getName().orElse(""))) {
            // We have a duplicate converter key name
            validationResult.addMetaFailure(i, new ValidationFailure(FailureReason.CONVERTER_KEY_DUPLICATION, metaItem.getName().orElse("")));
        } else {
            converterKeys.add(metaItem.getName().orElse(""));
        }
    });
    // Notify the embedded meta editor
    converterEditor.onValidationStateChange(validationResult);
    // Push AttributeLink update
    if (!validationResult.isValid()) {
        onValueModified.accept(null);
    } else {
        onValueModified.accept(new AttributeLink(attributeRef, convertMetaToConverter(converterAttribute.getMeta())));
    }
}
Also used : ValidationFailure(org.openremote.model.ValidationFailure)

Example 7 with ValidationFailure

use of org.openremote.model.ValidationFailure 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)

Example 8 with ValidationFailure

use of org.openremote.model.ValidationFailure in project openremote by openremote.

the class Simulator method writeView.

protected void writeView() {
    clear();
    addLabel(environment.getMessages().simulator());
    formGroups.clear();
    List<SimulatorElement> sortedElements = Arrays.asList(simulatorState.getElements());
    sortedElements.sort(Comparator.comparing(o -> simulatorState.getElementName(o)));
    for (SimulatorElement element : sortedElements) {
        FormGroup formGroup = new FormGroup();
        String elementName = simulatorState.getElementName(element);
        FormLabel formLabel = new FormLabel(elementName);
        formLabel.addStyleName("largest");
        formGroup.setFormLabel(formLabel);
        FormField formField = new FormField();
        formGroup.setFormField(formField);
        // Don't push simulator value validation up to the presenter as it is a special case that should
        // just be evaluated in-situ and shouldn't invalidate the parent attribute
        Consumer<Value> onModified = value -> {
            element.setValue(value);
            List<ValidationFailure> failures = element.getValidationFailures();
            formGroup.setError(failures != null && !failures.isEmpty());
        };
        ValueType valueType = element.getExpectedType().getValueType();
        IsWidget editor = valueEditorSupplier.createValueEditor(element, valueType, style, parentView, onModified);
        formField.add(editor);
        formGroups.put(element.getAttributeRef(), formGroup);
        add(formGroup);
    }
    if (sortedElements.size() > 0) {
        FormGroup submitGroup = new FormGroup();
        submitGroup.getElement().getStyle().setWidth(80, com.google.gwt.dom.client.Style.Unit.PCT);
        FormField submitField = new FormField();
        submitGroup.setFormField(submitField);
        FormButton writeButton = new FormButton(environment.getMessages().writeSimulatorState());
        writeButton.setPrimary(true);
        writeButton.addClickHandler(event -> {
            if (isValid()) {
                environment.getEventService().dispatch(simulatorState);
                environment.getEventBus().dispatch(new ShowSuccessEvent(environment.getMessages().simulatorStateSubmitted()));
            }
        });
        submitField.add(writeButton);
        add(submitGroup);
    } else {
        add(new FormInlineLabel(environment.getMessages().noAttributesLinkedToSimulator()));
    }
    // "Blink" the editor so users know there might be a new value
    for (FormGroup formGroup : formGroups.values()) {
        formGroup.addStyleName(environment.getWidgetStyle().HighlightBackground());
    }
    Browser.getWindow().setTimeout(() -> {
        for (FormGroup formGroup : formGroups.values()) formGroup.removeStyleName(environment.getWidgetStyle().HighlightBackground());
    }, 250);
}
Also used : Browser(elemental.client.Browser) EventRegistration(org.openremote.model.event.bus.EventRegistration) Consumer(org.openremote.model.interop.Consumer) SimulatorState(org.openremote.model.simulator.SimulatorState) java.util(java.util) ValueType(org.openremote.model.value.ValueType) Environment(org.openremote.app.client.Environment) AttributeRef(org.openremote.model.attribute.AttributeRef) ValidationFailure(org.openremote.model.ValidationFailure) org.openremote.app.client.widget(org.openremote.app.client.widget) AttributeViewImpl(org.openremote.app.client.assets.attributes.AttributeViewImpl) RequestSimulatorState(org.openremote.model.simulator.RequestSimulatorState) ShowSuccessEvent(org.openremote.app.client.event.ShowSuccessEvent) AttributeValidationResult(org.openremote.model.attribute.AttributeValidationResult) Value(org.openremote.model.value.Value) SimulatorElement(org.openremote.model.simulator.SimulatorElement) AbstractAttributeViewExtension(org.openremote.app.client.assets.attributes.AbstractAttributeViewExtension) IsWidget(com.google.gwt.user.client.ui.IsWidget) AssetAttribute(org.openremote.model.asset.AssetAttribute) AttributeView(org.openremote.app.client.assets.attributes.AttributeView) IsWidget(com.google.gwt.user.client.ui.IsWidget) ValueType(org.openremote.model.value.ValueType) SimulatorElement(org.openremote.model.simulator.SimulatorElement) ShowSuccessEvent(org.openremote.app.client.event.ShowSuccessEvent) Value(org.openremote.model.value.Value)

Example 9 with ValidationFailure

use of org.openremote.model.ValidationFailure in project openremote by openremote.

the class AttributeLinkEditor method showConverterEditorValidationError.

@SuppressWarnings("ParameterCanBeLocal")
protected void showConverterEditorValidationError(String attributeName, String metaItemName, ValidationFailure validationFailure) {
    // Replace metaItemName with AttributeLink
    metaItemName = environment.getMessages().metaItemDisplayName(AssetMeta.ATTRIBUTE_LINK.name());
    // Replace meta item validation failures
    if (validationFailure != null) {
        if (validationFailure.getReason() == MetaItem.MetaItemFailureReason.META_ITEM_NAME_IS_REQUIRED) {
            validationFailure = new ValidationFailure(FailureReason.CONVERTER_KEY_REQUIRED, validationFailure.getParameter().orElse(null));
        } else if (validationFailure.getReason() == MetaItem.MetaItemFailureReason.META_ITEM_VALUE_IS_REQUIRED) {
            validationFailure = new ValidationFailure(FailureReason.CONVERTER_VALUE_REQUIRED, validationFailure.getParameter().orElse(null));
        } else if (validationFailure.getReason() == MetaItem.MetaItemFailureReason.META_ITEM_VALUE_MISMATCH) {
            validationFailure = new ValidationFailure(FailureReason.CONVERTER_VALUE_MISMATCH, validationFailure.getParameter().orElse(null));
        }
    }
    validationErrorConsumer.accept(attributeName, metaItemName, validationFailure);
}
Also used : ValidationFailure(org.openremote.model.ValidationFailure)

Example 10 with ValidationFailure

use of org.openremote.model.ValidationFailure in project openremote by openremote.

the class MacroConfiguration method validateMacroConfiguration.

public static boolean validateMacroConfiguration(AssetAttribute attribute, AttributeValidationResult result) {
    boolean failure = false;
    if (!isMacroConfiguration(attribute)) {
        failure = true;
        if (result != null) {
            result.addAttributeFailure(new ValidationFailure(ValueHolder.ValueFailureReason.VALUE_MISMATCH, PROTOCOL_NAME));
        }
    }
    int actionCount = 0;
    if (attribute.getMeta() != null && !attribute.getMeta().isEmpty()) {
        for (int i = 0; i < attribute.getMeta().size(); i++) {
            MetaItem metaItem = attribute.getMeta().get(i);
            if (isMetaNameEqualTo(metaItem, META_MACRO_ACTION)) {
                actionCount++;
                if (!MacroAction.fromValue(metaItem.getValue().orElse(null)).isPresent()) {
                    failure = true;
                    if (result == null) {
                        break;
                    }
                    result.addMetaFailure(i, new ValidationFailure(MetaItem.MetaItemFailureReason.META_ITEM_VALUE_MISMATCH, "Macro Action"));
                }
            }
        }
    }
    if (actionCount == 0) {
        failure = true;
        if (result != null) {
            result.addMetaFailure(new ValidationFailure(MetaItem.MetaItemFailureReason.META_ITEM_MISSING, META_MACRO_ACTION));
        }
    }
    return !failure;
}
Also used : MetaItem(org.openremote.model.attribute.MetaItem) ValidationFailure(org.openremote.model.ValidationFailure)

Aggregations

ValidationFailure (org.openremote.model.ValidationFailure)15 MetaItem (org.openremote.model.attribute.MetaItem)6 Value (org.openremote.model.value.Value)5 AssetAttribute (org.openremote.model.asset.AssetAttribute)4 IsWidget (com.google.gwt.user.client.ui.IsWidget)3 java.util (java.util)3 Collectors (java.util.stream.Collectors)3 Environment (org.openremote.app.client.Environment)3 org.openremote.app.client.widget (org.openremote.app.client.widget)3 SimulatorElement (org.openremote.model.simulator.SimulatorElement)3 FlowPanel (com.google.gwt.user.client.ui.FlowPanel)2 IntStream (java.util.stream.IntStream)2 AssetMeta (org.openremote.model.asset.AssetMeta)2 AgentLink (org.openremote.model.asset.agent.AgentLink)2 ProtocolDescriptor (org.openremote.model.asset.agent.ProtocolDescriptor)2 AttributeValidationResult (org.openremote.model.attribute.AttributeValidationResult)2 MetaItemDescriptor (org.openremote.model.attribute.MetaItemDescriptor)2 Supplier (org.openremote.model.interop.Supplier)2 ColorSimulatorElement (org.openremote.model.simulator.element.ColorSimulatorElement)2 NumberSimulatorElement (org.openremote.model.simulator.element.NumberSimulatorElement)2