001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.scxml2.model; 018 019import java.util.LinkedHashMap; 020import java.util.Map; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.scxml2.ActionExecutionContext; 024import org.apache.commons.scxml2.Context; 025import org.apache.commons.scxml2.Evaluator; 026import org.apache.commons.scxml2.SCXMLExpressionException; 027import org.apache.commons.scxml2.SCXMLIOProcessor; 028import org.apache.commons.scxml2.SCXMLSystemContext; 029 030/** 031 * The class in this SCXML object model that corresponds to the 032 * <send> SCXML element. 033 * 034 */ 035public class Send extends NamelistHolder implements ContentContainer { 036 037 /** 038 * Serial version UID. 039 */ 040 private static final long serialVersionUID = 1L; 041 042 /** 043 * The suffix in the delay string for milliseconds. 044 */ 045 private static final String MILLIS = "ms"; 046 047 /** 048 * The suffix in the delay string for seconds. 049 */ 050 private static final String SECONDS = "s"; 051 052 /** 053 * The suffix in the delay string for minutes. 054 */ 055 private static final String MINUTES = "m"; 056 057 /** 058 * The number of milliseconds in a second. 059 */ 060 private static final long MILLIS_IN_A_SECOND = 1000L; 061 062 /** 063 * The number of milliseconds in a minute. 064 */ 065 private static final long MILLIS_IN_A_MINUTE = 60000L; 066 067 /** 068 * The ID of the send message. 069 */ 070 private String id; 071 072 /** 073 * Path expression evaluating to a location within a previously defined XML data tree. 074 */ 075 private String idlocation; 076 077 /** 078 * The target location of the event. 079 */ 080 private String target; 081 082 083 /** 084 * An expression specifying the target location of the event. 085 */ 086 private String targetexpr; 087 088 /** 089 * The type of the Event I/O Processor that the event should be dispatched to. 090 */ 091 private String type; 092 093 /** 094 * An expression defining the type of the Event I/O Processor that the event should be dispatched to. 095 */ 096 private String typeexpr; 097 098 /** 099 * The delay the event is dispatched after. 100 */ 101 private String delay; 102 103 /** 104 * An expression defining the delay the event is dispatched after. 105 */ 106 private String delayexpr; 107 108 /** 109 * The data containing information which may be used by the implementing platform to configure the event processor. 110 */ 111 private String hints; 112 113 /** 114 * The type of event being generated. 115 */ 116 private String event; 117 118 /** 119 * An expression defining the type of event being generated. 120 */ 121 private String eventexpr; 122 123 /** 124 * The <content/> of this send 125 */ 126 private Content content; 127 128 /** 129 * Constructor. 130 */ 131 public Send() { 132 super(); 133 } 134 135 /** 136 * @return the idlocation 137 */ 138 public String getIdlocation() { 139 return idlocation; 140 } 141 142 /** 143 * Set the idlocation expression 144 * 145 * @param idlocation The idlocation expression 146 */ 147 public void setIdlocation(final String idlocation) { 148 this.idlocation = idlocation; 149 } 150 151 /** 152 * Get the delay. 153 * 154 * @return Returns the delay. 155 */ 156 public final String getDelay() { 157 return delay; 158 } 159 160 /** 161 * Set the delay. 162 * 163 * @param delay The delay to set. 164 */ 165 public final void setDelay(final String delay) { 166 this.delay = delay; 167 } 168 169 /** 170 * @return The delay expression 171 */ 172 public String getDelayexpr() { 173 return delayexpr; 174 } 175 176 /** 177 * Set the delay expression 178 * 179 * @param delayexpr The delay expression to set 180 */ 181 public void setDelayexpr(final String delayexpr) { 182 this.delayexpr = delayexpr; 183 } 184 185 /** 186 * Get the hints for this <send> element. 187 * 188 * @return String Returns the hints. 189 */ 190 public final String getHints() { 191 return hints; 192 } 193 194 /** 195 * Set the hints for this <send> element. 196 * 197 * @param hints The hints to set. 198 */ 199 public final void setHints(final String hints) { 200 this.hints = hints; 201 } 202 203 /** 204 * Get the identifier for this <send> element. 205 * 206 * @return String Returns the id. 207 */ 208 public final String getId() { 209 return id; 210 } 211 212 /** 213 * Set the identifier for this <send> element. 214 * 215 * @param id The id to set. 216 */ 217 public final void setId(final String id) { 218 this.id = id; 219 } 220 221 /** 222 * Get the target for this <send> element. 223 * 224 * @return String Returns the target. 225 */ 226 public final String getTarget() { 227 return target; 228 } 229 230 /** 231 * Set the target for this <send> element. 232 * 233 * @param target The target to set. 234 */ 235 public final void setTarget(final String target) { 236 this.target = target; 237 } 238 239 /** 240 * @return The target expression 241 */ 242 public String getTargetexpr() { 243 return targetexpr; 244 } 245 246 /** 247 * Set the target expression 248 * 249 * @param targetexpr The target expression to set 250 */ 251 public void setTargetexpr(final String targetexpr) { 252 this.targetexpr = targetexpr; 253 } 254 255 /** 256 * Get the type for this <send> element. 257 * 258 * @return String Returns the type. 259 */ 260 public final String getType() { 261 return type; 262 } 263 264 /** 265 * Set the type for this <send> element. 266 * 267 * @param type The type to set. 268 */ 269 public final void setType(final String type) { 270 this.type = type; 271 } 272 273 /** 274 * @return The type expression 275 */ 276 public String getTypeexpr() { 277 return typeexpr; 278 } 279 280 /** 281 * Sets the type expression 282 * 283 * @param typeexpr The type expression to set 284 */ 285 public void setTypeexpr(final String typeexpr) { 286 this.typeexpr = typeexpr; 287 } 288 289 /** 290 * Get the event to send. 291 * 292 * @param event The event to set. 293 */ 294 public final void setEvent(final String event) { 295 this.event = event; 296 } 297 298 /** 299 * Set the event to send. 300 * 301 * @return String Returns the event. 302 */ 303 public final String getEvent() { 304 return event; 305 } 306 307 /** 308 * @return The event expression 309 */ 310 public String getEventexpr() { 311 return eventexpr; 312 } 313 314 /** 315 * Sets the event expression 316 * 317 * @param eventexpr The event expression to set 318 */ 319 public void setEventexpr(final String eventexpr) { 320 this.eventexpr = eventexpr; 321 } 322 323 /** 324 * Returns the content 325 * 326 * @return the content 327 */ 328 public Content getContent() { 329 return content; 330 } 331 332 /** 333 * Sets the content 334 * 335 * @param content the content to set 336 */ 337 public void setContent(final Content content) { 338 this.content = content; 339 } 340 341 /** 342 * {@inheritDoc} 343 */ 344 @SuppressWarnings("unchecked") 345 @Override 346 public void execute(ActionExecutionContext exctx) throws ModelException, SCXMLExpressionException { 347 // Send attributes evaluation 348 EnterableState parentState = getParentEnterableState(); 349 Context ctx = exctx.getContext(parentState); 350 ctx.setLocal(getNamespacesKey(), getNamespaces()); 351 Evaluator eval = exctx.getEvaluator(); 352 // Most attributes of <send> are expressions so need to be 353 // evaluated before the EventDispatcher callback 354 Object hintsValue = null; 355 if (hints != null) { 356 hintsValue = eval.eval(ctx, hints); 357 } 358 if (id == null) { 359 id = ctx.getSystemContext().generateSessionId(); 360 if (idlocation != null) { 361 eval.evalAssign(ctx, idlocation, id, Evaluator.AssignType.REPLACE_CHILDREN, null); 362 } 363 } 364 String targetValue = target; 365 if (targetValue == null && targetexpr != null) { 366 targetValue = (String) getTextContentIfNodeResult(eval.eval(ctx, targetexpr)); 367 if ((targetValue == null || targetValue.trim().length() == 0) 368 && exctx.getAppLog().isWarnEnabled()) { 369 exctx.getAppLog().warn("<send>: target expression \"" + targetexpr 370 + "\" evaluated to null or empty String"); 371 } 372 } 373 String typeValue = type; 374 if (typeValue == null && typeexpr != null) { 375 typeValue = (String) getTextContentIfNodeResult(eval.eval(ctx, typeexpr)); 376 if ((typeValue == null || typeValue.trim().length() == 0) 377 && exctx.getAppLog().isWarnEnabled()) { 378 exctx.getAppLog().warn("<send>: type expression \"" + typeexpr 379 + "\" evaluated to null or empty String"); 380 } 381 } 382 if (typeValue == null) { 383 // must default to 'scxml' when unspecified 384 typeValue = SCXMLIOProcessor.DEFAULT_EVENT_PROCESSOR; 385 } else if (!SCXMLIOProcessor.DEFAULT_EVENT_PROCESSOR.equals(typeValue) && typeValue.trim().equalsIgnoreCase(SCXMLIOProcessor.SCXML_EVENT_PROCESSOR)) { 386 typeValue = SCXMLIOProcessor.DEFAULT_EVENT_PROCESSOR; 387 } 388 Object payload = null; 389 Map<String, Object> payloadDataMap = new LinkedHashMap<String, Object>(); 390 addNamelistDataToPayload(exctx, payloadDataMap); 391 addParamsToPayload(exctx, payloadDataMap); 392 if (!payloadDataMap.isEmpty()) { 393 payload = makeEventPayload(eval, payloadDataMap); 394 } 395 else if (content != null) { 396 if (content.getExpr() != null) { 397 payload = clonePayloadValue(eval.eval(ctx, content.getExpr())); 398 } else { 399 payload = clonePayloadValue(content.getBody()); 400 } 401 } 402 long wait = 0L; 403 String delayString = delay; 404 if (delayString == null && delayexpr != null) { 405 Object delayValue = getTextContentIfNodeResult(eval.eval(ctx, delayexpr)); 406 if (delayValue != null) { 407 delayString = delayValue.toString(); 408 } 409 } 410 if (delayString != null) { 411 wait = parseDelay(delayString, exctx.getAppLog()); 412 } 413 String eventValue = event; 414 if (eventValue == null && eventexpr != null) { 415 eventValue = (String) getTextContentIfNodeResult(eval.eval(ctx, eventexpr)); 416 if ((eventValue == null)) { 417 throw new SCXMLExpressionException("<send>: event expression \"" + eventexpr 418 + "\" evaluated to null"); 419 } 420 } 421 Map<String, SCXMLIOProcessor> ioProcessors = (Map<String, SCXMLIOProcessor>) ctx.get(SCXMLSystemContext.IOPROCESSORS_KEY); 422 ctx.setLocal(getNamespacesKey(), null); 423 if (exctx.getAppLog().isDebugEnabled()) { 424 exctx.getAppLog().debug("<send>: Dispatching event '" + eventValue 425 + "' to target '" + targetValue + "' of target type '" 426 + typeValue + "' with suggested delay of " + wait 427 + "ms"); 428 } 429 exctx.getEventDispatcher().send(ioProcessors, id, targetValue, typeValue, eventValue, 430 payload, hintsValue, wait); 431 } 432 433 /** 434 * Parse delay. 435 * 436 * @param delayString The String value of the delay, in CSS2 format 437 * @param appLog The application log 438 * @return The parsed delay in milliseconds 439 * @throws SCXMLExpressionException If the delay cannot be parsed 440 */ 441 private long parseDelay(final String delayString, final Log appLog) 442 throws SCXMLExpressionException { 443 444 long wait = 0L; 445 long multiplier = 1L; 446 447 if (delayString != null && delayString.trim().length() > 0) { 448 449 String trimDelay = delayString.trim(); 450 String numericDelay = trimDelay; 451 if (trimDelay.endsWith(MILLIS)) { 452 numericDelay = trimDelay.substring(0, trimDelay.length() - 2); 453 } else if (trimDelay.endsWith(SECONDS)) { 454 multiplier = MILLIS_IN_A_SECOND; 455 numericDelay = trimDelay.substring(0, trimDelay.length() - 1); 456 } else if (trimDelay.endsWith(MINUTES)) { // Not CSS2 457 multiplier = MILLIS_IN_A_MINUTE; 458 numericDelay = trimDelay.substring(0, trimDelay.length() - 1); 459 } 460 461 try { 462 wait = Long.parseLong(numericDelay); 463 } catch (NumberFormatException nfe) { 464 appLog.error(nfe.getMessage(), nfe); 465 throw new SCXMLExpressionException(nfe.getMessage(), nfe); 466 } 467 wait *= multiplier; 468 469 } 470 return wait; 471 } 472} 473