| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.scxml2.model; |
| |
| import java.util.ArrayList; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.commons.scxml2.ActionExecutionContext; |
| import org.apache.commons.scxml2.Context; |
| import org.apache.commons.scxml2.Evaluator; |
| import org.apache.commons.scxml2.EventBuilder; |
| import org.apache.commons.scxml2.SCXMLExpressionException; |
| import org.apache.commons.scxml2.SCXMLIOProcessor; |
| import org.apache.commons.scxml2.SCXMLSystemContext; |
| import org.apache.commons.scxml2.TriggerEvent; |
| import org.apache.commons.scxml2.semantics.ErrorConstants; |
| |
| /** |
| * The class in this SCXML object model that corresponds to the |
| * <send> SCXML element. |
| * |
| */ |
| public class Send extends Action implements ContentContainer, ParamsContainer { |
| |
| /** |
| * Serial version UID. |
| */ |
| private static final long serialVersionUID = 1L; |
| |
| /** |
| * The suffix in the delay string for milliseconds. |
| */ |
| private static final String MILLIS = "ms"; |
| |
| /** |
| * The suffix in the delay string for seconds. |
| */ |
| private static final String SECONDS = "s"; |
| |
| /** |
| * The suffix in the delay string for minutes. |
| */ |
| private static final String MINUTES = "m"; |
| |
| /** |
| * The number of milliseconds in a second. |
| */ |
| private static final long MILLIS_IN_A_SECOND = 1000L; |
| |
| /** |
| * The number of milliseconds in a minute. |
| */ |
| private static final long MILLIS_IN_A_MINUTE = 60000L; |
| |
| /** |
| * The ID of the send message. |
| */ |
| private String id; |
| |
| /** |
| * Path expression evaluating to a location within a previously defined XML data tree. |
| */ |
| private String idlocation; |
| |
| /** |
| * The target location of the event. |
| */ |
| private String target; |
| |
| |
| /** |
| * An expression specifying the target location of the event. |
| */ |
| private String targetexpr; |
| |
| /** |
| * The type of the Event I/O Processor that the event should be dispatched to. |
| */ |
| private String type; |
| |
| /** |
| * An expression defining the type of the Event I/O Processor that the event should be dispatched to. |
| */ |
| private String typeexpr; |
| |
| /** |
| * The delay the event is dispatched after. |
| */ |
| private String delay; |
| |
| /** |
| * An expression defining the delay the event is dispatched after. |
| */ |
| private String delayexpr; |
| |
| /** |
| * The data containing information which may be used by the implementing platform to configure the event processor. |
| */ |
| private String hints; |
| |
| /** |
| * The type of event being generated. |
| */ |
| private String event; |
| |
| /** |
| * An expression defining the type of event being generated. |
| */ |
| private String eventexpr; |
| |
| /** |
| * The <content/> of this send |
| */ |
| private Content content; |
| |
| /** |
| * The List of the params to be sent |
| */ |
| private final List<Param> paramsList = new ArrayList<>(); |
| |
| /** |
| * The namelist. |
| */ |
| private String namelist; |
| |
| /** |
| * Constructor. |
| */ |
| public Send() { |
| } |
| |
| /** |
| * @return the idlocation |
| */ |
| public String getIdlocation() { |
| return idlocation; |
| } |
| |
| /** |
| * Set the idlocation expression |
| * |
| * @param idlocation The idlocation expression |
| */ |
| public void setIdlocation(final String idlocation) { |
| this.idlocation = idlocation; |
| } |
| |
| /** |
| * Get the delay. |
| * |
| * @return Returns the delay. |
| */ |
| public final String getDelay() { |
| return delay; |
| } |
| |
| /** |
| * Set the delay. |
| * |
| * @param delay The delay to set. |
| */ |
| public final void setDelay(final String delay) { |
| this.delay = delay; |
| } |
| |
| /** |
| * @return The delay expression |
| */ |
| public String getDelayexpr() { |
| return delayexpr; |
| } |
| |
| /** |
| * Set the delay expression |
| * |
| * @param delayexpr The delay expression to set |
| */ |
| public void setDelayexpr(final String delayexpr) { |
| this.delayexpr = delayexpr; |
| } |
| |
| /** |
| * Get the hints for this <send> element. |
| * |
| * @return String Returns the hints. |
| */ |
| public final String getHints() { |
| return hints; |
| } |
| |
| /** |
| * Set the hints for this <send> element. |
| * |
| * @param hints The hints to set. |
| */ |
| public final void setHints(final String hints) { |
| this.hints = hints; |
| } |
| |
| /** |
| * Get the identifier for this <send> element. |
| * |
| * @return String Returns the id. |
| */ |
| public final String getId() { |
| return id; |
| } |
| |
| /** |
| * Set the identifier for this <send> element. |
| * |
| * @param id The id to set. |
| */ |
| public final void setId(final String id) { |
| this.id = id; |
| } |
| |
| /** |
| * Get the target for this <send> element. |
| * |
| * @return String Returns the target. |
| */ |
| public final String getTarget() { |
| return target; |
| } |
| |
| /** |
| * Set the target for this <send> element. |
| * |
| * @param target The target to set. |
| */ |
| public final void setTarget(final String target) { |
| this.target = target; |
| } |
| |
| /** |
| * @return The target expression |
| */ |
| public String getTargetexpr() { |
| return targetexpr; |
| } |
| |
| /** |
| * Set the target expression |
| * |
| * @param targetexpr The target expression to set |
| */ |
| public void setTargetexpr(final String targetexpr) { |
| this.targetexpr = targetexpr; |
| } |
| |
| /** |
| * Get the type for this <send> element. |
| * |
| * @return String Returns the type. |
| */ |
| public final String getType() { |
| return type; |
| } |
| |
| /** |
| * Set the type for this <send> element. |
| * |
| * @param type The type to set. |
| */ |
| public final void setType(final String type) { |
| this.type = type; |
| } |
| |
| /** |
| * @return The type expression |
| */ |
| public String getTypeexpr() { |
| return typeexpr; |
| } |
| |
| /** |
| * Sets the type expression |
| * |
| * @param typeexpr The type expression to set |
| */ |
| public void setTypeexpr(final String typeexpr) { |
| this.typeexpr = typeexpr; |
| } |
| |
| /** |
| * Get the event to send. |
| * |
| * @param event The event to set. |
| */ |
| public final void setEvent(final String event) { |
| this.event = event; |
| } |
| |
| /** |
| * Set the event to send. |
| * |
| * @return String Returns the event. |
| */ |
| public final String getEvent() { |
| return event; |
| } |
| |
| /** |
| * @return The event expression |
| */ |
| public String getEventexpr() { |
| return eventexpr; |
| } |
| |
| /** |
| * Sets the event expression |
| * |
| * @param eventexpr The event expression to set |
| */ |
| public void setEventexpr(final String eventexpr) { |
| this.eventexpr = eventexpr; |
| } |
| |
| /** |
| * Returns the content |
| * |
| * @return the content |
| */ |
| public Content getContent() { |
| return content; |
| } |
| |
| /** |
| * Sets the content |
| * |
| * @param content the content to set |
| */ |
| public void setContent(final Content content) { |
| this.content = content; |
| } |
| |
| /** |
| * Get the list of {@link Param}s. |
| * |
| * @return List The params list. |
| */ |
| public List<Param> getParams() { |
| return paramsList; |
| } |
| |
| /** |
| * Get the namelist. |
| * |
| * @return String Returns the namelist. |
| */ |
| public final String getNamelist() { |
| return namelist; |
| } |
| |
| /** |
| * Set the namelist. |
| * |
| * @param namelist The namelist to set. |
| */ |
| public final void setNamelist(final String namelist) { |
| this.namelist = namelist; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @SuppressWarnings("unchecked") |
| @Override |
| public void execute(ActionExecutionContext exctx) throws ModelException, SCXMLExpressionException { |
| // Send attributes evaluation |
| EnterableState parentState = getParentEnterableState(); |
| Context ctx = exctx.getContext(parentState); |
| Evaluator eval = exctx.getEvaluator(); |
| // Most attributes of <send> are expressions so need to be |
| // evaluated before the EventDispatcher callback |
| Object hintsValue = null; |
| if (hints != null) { |
| hintsValue = eval.eval(ctx, hints); |
| } |
| if (idlocation != null) { |
| if (id == null) { |
| id = ctx.getSystemContext().generateSessionId(); |
| } |
| eval.evalAssign(ctx, idlocation, id); |
| } |
| String targetValue = target; |
| if (targetValue == null && targetexpr != null) { |
| targetValue = (String)eval.eval(ctx, targetexpr); |
| if ((targetValue == null || targetValue.trim().length() == 0) |
| && exctx.getAppLog().isWarnEnabled()) { |
| exctx.getAppLog().warn("<send>: target expression \"" + targetexpr |
| + "\" evaluated to null or empty String"); |
| } |
| } |
| String typeValue = type; |
| if (typeValue == null && typeexpr != null) { |
| typeValue = (String)eval.eval(ctx, typeexpr); |
| if ((typeValue == null || typeValue.trim().length() == 0) |
| && exctx.getAppLog().isWarnEnabled()) { |
| exctx.getAppLog().warn("<send>: type expression \"" + typeexpr |
| + "\" evaluated to null or empty String"); |
| } |
| } |
| if (typeValue == null) { |
| // must default to 'scxml' when unspecified |
| typeValue = SCXMLIOProcessor.DEFAULT_EVENT_PROCESSOR; |
| } else if (!SCXMLIOProcessor.DEFAULT_EVENT_PROCESSOR.equals(typeValue) && typeValue.trim().equalsIgnoreCase(SCXMLIOProcessor.SCXML_EVENT_PROCESSOR)) { |
| typeValue = SCXMLIOProcessor.DEFAULT_EVENT_PROCESSOR; |
| } |
| Object payload = null; |
| Map<String, Object> payloadDataMap = new LinkedHashMap<>(); |
| PayloadBuilder.addNamelistDataToPayload(parentState, ctx, eval, exctx.getErrorReporter(), namelist, payloadDataMap); |
| PayloadBuilder.addParamsToPayload(ctx, eval, paramsList, payloadDataMap); |
| if (!payloadDataMap.isEmpty()) { |
| payload = payloadDataMap; |
| } |
| else if (content != null) { |
| if (content.getExpr() != null) { |
| Object evalResult; |
| try { |
| evalResult = eval.eval(ctx, content.getExpr()); |
| } catch (SCXMLExpressionException e) { |
| exctx.getInternalIOProcessor().addEvent(new EventBuilder(TriggerEvent.ERROR_EXECUTION, |
| TriggerEvent.ERROR_EVENT).build()); |
| exctx.getErrorReporter().onError(ErrorConstants.EXPRESSION_ERROR, |
| "Failed to evaluate <send> <content> expression due to error: "+ e.getMessage() |
| + ", Using empty value instead.", getParent()); |
| evalResult = ""; |
| } |
| payload = eval.cloneData(evalResult); |
| } else if (content.getParsedValue() != null) { |
| payload = content.getParsedValue().getValue(); |
| } |
| } |
| long wait = 0L; |
| String delayString = delay; |
| if (delayString == null && delayexpr != null) { |
| Object delayValue = eval.eval(ctx, delayexpr); |
| if (delayValue != null) { |
| delayString = delayValue.toString(); |
| } |
| } |
| if (delayString != null) { |
| wait = parseDelay(delayString, delayexpr != null, delayexpr != null ? delayexpr : delay); |
| } |
| String eventValue = event; |
| if (eventValue == null && eventexpr != null) { |
| eventValue = (String)eval.eval(ctx, eventexpr); |
| if ((eventValue == null)) { |
| throw new SCXMLExpressionException("<send>: event expression \"" + eventexpr |
| + "\" evaluated to null"); |
| } |
| } |
| Map<String, SCXMLIOProcessor> ioProcessors = (Map<String, SCXMLIOProcessor>) ctx.get(SCXMLSystemContext.IOPROCESSORS_KEY); |
| if (exctx.getAppLog().isDebugEnabled()) { |
| exctx.getAppLog().debug("<send>: Dispatching event '" + eventValue |
| + "' to target '" + targetValue + "' of target type '" |
| + typeValue + "' with suggested delay of " + wait |
| + "ms"); |
| } |
| exctx.getEventDispatcher().send(ioProcessors, id, targetValue, typeValue, eventValue, |
| payload, hintsValue, wait); |
| } |
| |
| /** |
| * Parse delay. |
| * |
| * @param delayString The String value of the delay, in CSS2 format |
| * @param expression indicates if this is for a delayexpr or delay attribute |
| * @param delayStringSource the original delayString source (delayString might be different in case of a delayexpr) |
| * @return The parsed delay in milliseconds |
| * @throws SCXMLExpressionException If the delay cannot be parsed |
| */ |
| static long parseDelay(final String delayString, final boolean expression, final String delayStringSource) |
| throws SCXMLExpressionException { |
| |
| long wait = 0L; |
| long multiplier = 1L; |
| |
| if (delayString != null && delayString.trim().length() > 0) { |
| |
| try { |
| String trimDelay = delayString.trim(); |
| String numericDelay = trimDelay; |
| if (trimDelay.endsWith(MILLIS)) { |
| numericDelay = trimDelay.substring(0, trimDelay.length() - 2); |
| } else if (trimDelay.endsWith(SECONDS)) { |
| multiplier = multiplier*MILLIS_IN_A_SECOND; |
| numericDelay = trimDelay.substring(0, trimDelay.length() - 1); |
| } else if (trimDelay.endsWith(MINUTES)) { // Not CSS2 |
| multiplier = multiplier*MILLIS_IN_A_MINUTE; |
| numericDelay = trimDelay.substring(0, trimDelay.length() - 1); |
| } |
| int fractionIndex = numericDelay.indexOf('.'); |
| if (fractionIndex > -1) { |
| if (fractionIndex > 0) { |
| wait = Long.parseLong(numericDelay.substring(0, fractionIndex)); |
| wait *= multiplier; |
| } |
| numericDelay = numericDelay.substring(fractionIndex+1); |
| multiplier /= Math.pow(10, numericDelay.length()); |
| } |
| if (numericDelay.length() > 0) { |
| wait += Long.parseLong(numericDelay) * multiplier; |
| } |
| } catch (NumberFormatException nfe) { |
| throw new SCXMLExpressionException("<send>: invalid " + (expression ? "delayexpr=\"" : "delay=\"") + delayStringSource +"\""); |
| } |
| } |
| return wait; |
| } |
| } |