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.HashMap; 020import java.util.Map; 021 022import org.apache.commons.scxml2.ActionExecutionContext; 023import org.apache.commons.scxml2.Context; 024import org.apache.commons.scxml2.Evaluator; 025import org.apache.commons.scxml2.PathResolver; 026import org.apache.commons.scxml2.SCXMLExecutionContext; 027import org.apache.commons.scxml2.SCXMLExpressionException; 028import org.apache.commons.scxml2.SCXMLSystemContext; 029import org.apache.commons.scxml2.TriggerEvent; 030import org.apache.commons.scxml2.invoke.Invoker; 031import org.apache.commons.scxml2.invoke.InvokerException; 032import org.apache.commons.scxml2.semantics.ErrorConstants; 033import org.w3c.dom.Node; 034 035/** 036 * The class in this SCXML object model that corresponds to the 037 * <invoke> SCXML element. 038 * 039 */ 040public class Invoke extends NamelistHolder implements PathResolverHolder, ContentContainer { 041 042 /** 043 * Serial version UID. 044 */ 045 private static final long serialVersionUID = 1L; 046 047 /** 048 * The default context variable key under which the current SCXMLExecutionContext is provided 049 */ 050 private static final String CURRENT_EXECUTION_CONTEXT_KEY = "_CURRENT_EXECUTION_CONTEXT"; 051 052 /** 053 * Identifier for this Invoke. 054 * */ 055 private String id; 056 057 /** 058 * Path expression evaluating to a location within a previously defined XML data tree. 059 */ 060 private String idlocation; 061 062 /** 063 * The type of target to be invoked. 064 */ 065 private String type; 066 067 /** 068 * An expression defining the type of the target to be invoked. 069 */ 070 private String typeexpr; 071 072 /** 073 * The source URL for the external service. 074 */ 075 private String src; 076 077 /** 078 * The expression that evaluates to the source URL for the 079 * external service. 080 */ 081 private String srcexpr; 082 083 /** 084 * A flag indicating whether to forward events to the invoked process. 085 */ 086 private Boolean autoForward; 087 088 /** 089 * The <finalize> child, may be null. 090 */ 091 private Finalize finalize; 092 093 /** 094 * {@link PathResolver} for resolving the "src" or "srcexpr" result. 095 */ 096 private PathResolver pathResolver; 097 098 /** 099 * The <content/> of this invoke 100 */ 101 private Content content; 102 103 private EnterableState parent; 104 105 /** 106 * Get the identifier for this invoke (may be null). 107 * 108 * @return Returns the id. 109 */ 110 public final String getId() { 111 return id; 112 } 113 114 /** 115 * Set the identifier for this invoke. 116 * 117 * @param id The id to set. 118 */ 119 public final void setId(final String id) { 120 this.id = id; 121 } 122 123 /** 124 * @return the idlocation 125 */ 126 public String getIdlocation() { 127 return idlocation; 128 } 129 130 /** 131 * Set the idlocation expression 132 * @param idlocation The idlocation expression 133 */ 134 public void setIdlocation(final String idlocation) { 135 this.idlocation = idlocation; 136 } 137 138 /** 139 * Get the type for this <invoke> element. 140 * 141 * @return String Returns the type. 142 */ 143 public final String getType() { 144 return type; 145 } 146 147 /** 148 * Set the type for this <invoke> element. 149 * 150 * @param type The type to set. 151 */ 152 public final void setType(final String type) { 153 this.type = type; 154 } 155 156 /** 157 * @return The type expression 158 */ 159 public String getTypeexpr() { 160 return typeexpr; 161 } 162 163 /** 164 * Sets the type expression 165 * @param typeexpr The type expression to set 166 */ 167 public void setTypeexpr(final String typeexpr) { 168 this.typeexpr = typeexpr; 169 } 170 171 /** 172 * Get the URL for the external service. 173 * 174 * @return String The URL. 175 */ 176 public final String getSrc() { 177 return src; 178 } 179 180 /** 181 * Set the URL for the external service. 182 * 183 * @param src The source URL. 184 */ 185 public final void setSrc(final String src) { 186 this.src = src; 187 } 188 189 /** 190 * Get the expression that evaluates to the source URL for the 191 * external service. 192 * 193 * @return String The source expression. 194 */ 195 public final String getSrcexpr() { 196 return srcexpr; 197 } 198 199 /** 200 * Set the expression that evaluates to the source URL for the 201 * external service. 202 * 203 * @param srcexpr The source expression. 204 */ 205 public final void setSrcexpr(final String srcexpr) { 206 this.srcexpr = srcexpr; 207 } 208 209 210 /** 211 * @return Returns true if all external events should be forwarded to the invoked process. 212 */ 213 public final boolean isAutoForward() { 214 return autoForward != null && autoForward; 215 } 216 217 /** 218 * @return Returns the flag indicating whether to forward events to the invoked process. 219 */ 220 public final Boolean getAutoForward() { 221 return autoForward; 222 } 223 224 /** 225 * Set the flag indicating whether to forward events to the invoked process. 226 * @param autoForward the flag 227 */ 228 public final void setAutoForward(final Boolean autoForward) { 229 this.autoForward = autoForward; 230 } 231 232 /** 233 * Get the Finalize for this Invoke. 234 * 235 * @return Finalize The Finalize for this Invoke. 236 */ 237 public final Finalize getFinalize() { 238 return finalize; 239 } 240 241 /** 242 * Set the Finalize for this Invoke. 243 * 244 * @param finalize The Finalize for this Invoke. 245 */ 246 public final void setFinalize(final Finalize finalize) { 247 this.finalize = finalize; 248 } 249 250 /** 251 * Get the {@link PathResolver}. 252 * 253 * @return Returns the pathResolver. 254 */ 255 public PathResolver getPathResolver() { 256 return pathResolver; 257 } 258 259 /** 260 * Set the {@link PathResolver}. 261 * 262 * @param pathResolver The pathResolver to set. 263 */ 264 public void setPathResolver(final PathResolver pathResolver) { 265 this.pathResolver = pathResolver; 266 } 267 268 /** 269 * Enforce identity equality only 270 * @param other other object to compare with 271 * @return this == other 272 */ 273 @Override 274 public final boolean equals(final Object other) { 275 return this == other; 276 } 277 278 /** 279 * Enforce returning identity based hascode 280 * @return {@link System#identityHashCode(Object) System.identityHashCode(this)} 281 */ 282 @Override 283 public final int hashCode() { 284 return System.identityHashCode(this); 285 } 286 287 /** 288 * Returns the content 289 * 290 * @return the content 291 */ 292 public Content getContent() { 293 return content; 294 } 295 296 /** 297 * @return The local context variable name under which the current SCXMLExecutionContext is provided to the Invoke 298 */ 299 public String getCurrentSCXMLExecutionContextKey() { 300 return CURRENT_EXECUTION_CONTEXT_KEY; 301 } 302 303 /** 304 * Sets the content 305 * 306 * @param content the content to set 307 */ 308 public void setContent(final Content content) { 309 this.content = content; 310 } 311 312 /** 313 * Get the parent EnterableState. 314 * 315 * @return Returns the parent state 316 */ 317 public EnterableState getParentEnterableState() { 318 return parent; 319 } 320 321 /** 322 * Set the parent EnterableState. 323 * @param parent The parent state to set 324 */ 325 public void setParentEnterableState(final EnterableState parent) { 326 if (parent == null) { 327 throw new IllegalArgumentException("Parent parameter cannot be null"); 328 } 329 this.parent = parent; 330 } 331 332 @SuppressWarnings("unchecked") 333 @Override 334 public void execute(final ActionExecutionContext axctx) throws ModelException { 335 EnterableState parentState = getParentEnterableState(); 336 Context ctx = axctx.getContext(parentState); 337 SCXMLExecutionContext exctx = (SCXMLExecutionContext)ctx.getVars().get(getCurrentSCXMLExecutionContextKey()); 338 if (exctx == null) { 339 throw new ModelException("Missing current SCXMLExecutionContext instance in context under key: "+ getCurrentSCXMLExecutionContextKey()); 340 } 341 try { 342 ctx.setLocal(getNamespacesKey(), getNamespaces()); 343 Evaluator eval = axctx.getEvaluator(); 344 345 String typeValue = type; 346 if (typeValue == null && typeexpr != null) { 347 typeValue = (String) getTextContentIfNodeResult(eval.eval(ctx, typeexpr)); 348 if (typeValue == null) { 349 throw new SCXMLExpressionException("<invoke> for state "+parentState.getId() + 350 ": type expression \"" + typeexpr + "\" evaluated to null or empty String"); 351 } 352 } 353 if (typeValue == null) { 354 typeValue = SCXMLExecutionContext.SCXML_INVOKER_TYPE; 355 } 356 Invoker invoker = exctx.newInvoker(typeValue); 357 358 String invokeId = getId(); 359 if (invokeId == null) { 360 invokeId = parentState.getId() + "." + ctx.get(SCXMLSystemContext.SESSIONID_KEY); 361 } 362 if (getId() == null && getIdlocation() != null) { 363 eval.evalAssign(ctx, idlocation, invokeId, Evaluator.AssignType.REPLACE_CHILDREN, null); 364 } 365 invoker.setInvokeId(invokeId); 366 367 String src = getSrc(); 368 if (src == null && getSrcexpr() != null) { 369 src = (String) getTextContentIfNodeResult(eval.eval(ctx, getSrcexpr())); 370 } 371 if (src != null) { 372 PathResolver pr = getPathResolver(); 373 if (pr != null) { 374 src = getPathResolver().resolvePath(src); 375 } 376 } 377 Node srcNode = null; 378 if (src == null && getContent() != null) { 379 Object contentValue; 380 if (content.getExpr() != null) { 381 contentValue = eval.eval(ctx, content.getExpr()); 382 } else { 383 contentValue = content.getBody(); 384 } 385 if (contentValue instanceof Node) { 386 srcNode = ((Node)contentValue).cloneNode(true); 387 } 388 else if (contentValue != null) { 389 src = String.valueOf(contentValue); 390 } 391 } 392 if (src == null && srcNode == null) { 393 throw new SCXMLExpressionException("<invoke> for state "+parentState.getId() + 394 ": no src and no content defined"); 395 } 396 Map<String, Object> payloadDataMap = new HashMap<String, Object>(); 397 addNamelistDataToPayload(axctx, payloadDataMap); 398 addParamsToPayload(axctx, payloadDataMap); 399 invoker.setParentSCXMLExecutor(exctx.getSCXMLExecutor()); 400 if (src != null) { 401 invoker.invoke(src, payloadDataMap); 402 } 403 // TODO: } else { invoker.invoke(srcNode, payloadDataMap); } 404 exctx.registerInvoker(this, invoker); 405 } 406 catch (InvokerException e) { 407 axctx.getErrorReporter().onError(ErrorConstants.EXECUTION_ERROR, e.getMessage(), this); 408 axctx.getInternalIOProcessor().addEvent(new TriggerEvent(TriggerEvent.ERROR_EXECUTION, TriggerEvent.ERROR_EVENT)); 409 } 410 catch (SCXMLExpressionException e) { 411 axctx.getInternalIOProcessor().addEvent(new TriggerEvent(TriggerEvent.ERROR_EXECUTION, TriggerEvent.ERROR_EVENT)); 412 axctx.getErrorReporter().onError(ErrorConstants.EXPRESSION_ERROR, e.getMessage(), this); 413 } 414 finally { 415 ctx.setLocal(getNamespacesKey(), null); 416 } 417 } 418}