| /* |
| * 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.ode.bpel.runtime; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.ode.bpel.common.FaultException; |
| import org.apache.ode.bpel.evt.ScopeEvent; |
| import org.apache.ode.bpel.iapi.BpelEngineException; |
| import org.apache.ode.bpel.obj.OElementVarType; |
| import org.apache.ode.bpel.obj.OMessageVarType; |
| import org.apache.ode.bpel.obj.OPartnerLink; |
| import org.apache.ode.bpel.obj.OScope; |
| import org.apache.ode.bpel.obj.OMessageVarType.Part; |
| import org.apache.ode.bpel.runtime.BpelRuntimeContext.ValueReferencePair; |
| import org.apache.ode.bpel.runtime.channels.FaultData; |
| import org.apache.ode.utils.DOMUtils; |
| import org.apache.ode.bpel.evar.ExternalVariableModuleException; |
| import org.apache.ode.bpel.evar.IncompleteKeyException; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.Set; |
| |
| import javax.xml.namespace.QName; |
| |
| |
| /** |
| * N-tuple representing a scope "frame" (as in stack frame). |
| */ |
| class ScopeFrame implements Serializable { |
| private static final long serialVersionUID = 1L; |
| private static final Log __log = LogFactory.getLog(ScopeFrame.class); |
| |
| /** The compiled scope representation. */ |
| final OScope oscope; |
| |
| /** The parent scope frame. */ |
| final ScopeFrame parent; |
| |
| /** Database scope instance identifier. */ |
| final Long scopeInstanceId; |
| |
| Set<CompensationHandler> availableCompensations; |
| |
| /** The fault context for this scope. */ |
| private FaultData _faultData; |
| |
| final InstanceGlobals globals; |
| |
| /** Constructor used to create "fault" scopes. */ |
| ScopeFrame( OScope scopeDef, |
| Long scopeInstanceId, |
| ScopeFrame parent, |
| Set<CompensationHandler> visibleCompensationHandlers, |
| FaultData fault) { |
| this(scopeDef,scopeInstanceId,parent,visibleCompensationHandlers, parent.globals); |
| _faultData = fault; |
| |
| } |
| public ScopeFrame( OScope scopeDef, |
| Long scopeInstanceId, |
| ScopeFrame parent, |
| Set<CompensationHandler> visibleCompensationHandlers) { |
| this(scopeDef,scopeInstanceId,parent,visibleCompensationHandlers,parent.globals); |
| } |
| |
| public ScopeFrame( OScope scopeDef, |
| Long scopeInstanceId, |
| ScopeFrame parent, |
| Set<CompensationHandler> visibleCompensationHandlers, |
| InstanceGlobals globals) { |
| this.oscope = scopeDef; |
| this.scopeInstanceId = scopeInstanceId; |
| this.parent = parent; |
| this.availableCompensations = visibleCompensationHandlers; |
| this.globals = globals; |
| } |
| |
| |
| public ScopeFrame find(OScope scope) { |
| if (oscope.getName().equals(scope.getName())) { |
| return this; |
| } |
| |
| return (parent != null) |
| ? parent.find(scope) |
| : null; |
| } |
| |
| public VariableInstance resolve(OScope.Variable variable) { |
| ScopeFrame scopeFrame = find(variable.getDeclaringScope()); |
| if (scopeFrame == null) return null; |
| return new VariableInstance(scopeFrame.scopeInstanceId, variable); |
| } |
| |
| public CorrelationSetInstance resolve(OScope.CorrelationSet cset) { |
| return new CorrelationSetInstance(find(cset.getDeclaringScope()).scopeInstanceId, cset); |
| } |
| |
| public PartnerLinkInstance resolve(OPartnerLink partnerLink) { |
| return new PartnerLinkInstance(find(partnerLink.getDeclaringScope()).scopeInstanceId, partnerLink); |
| } |
| |
| public String toString() { |
| StringBuffer buf= new StringBuffer("{ScopeFrame: o="); |
| buf.append(oscope); |
| buf.append(", id="); |
| buf.append(scopeInstanceId); |
| if (availableCompensations != null) { |
| buf.append(", avComps="); |
| buf.append(availableCompensations); |
| } |
| if (_faultData != null) { |
| buf.append(", fault="); |
| buf.append(_faultData); |
| } |
| buf.append('}'); |
| return buf.toString(); |
| } |
| |
| public FaultData getFault() { |
| if (_faultData != null) |
| return _faultData; |
| if (parent != null) |
| return parent.getFault(); |
| return null; |
| } |
| |
| public void fillEventInfo(ScopeEvent event) { |
| ScopeFrame currentScope = this; |
| ArrayList<String> parentNames = new ArrayList<String>(); |
| while (currentScope != null) { |
| parentNames.add(currentScope.oscope.getName()); |
| currentScope = currentScope.parent; |
| } |
| event.setParentScopesNames(parentNames); |
| if (parent != null) |
| event.setParentScopeId(parent.scopeInstanceId); |
| event.setScopeId(scopeInstanceId); |
| event.setScopeName(oscope.getName()); |
| event.setScopeDeclerationId(oscope.getId()); |
| if (event.getLineNo() == -1 && oscope.getDebugInfo() != null) |
| event.setLineNo(oscope.getDebugInfo().getStartLine()); |
| } |
| |
| |
| // |
| // Move all the external variable stuff in here so that it can be used by the expr-lang evaluation |
| // context. |
| // |
| |
| Node fetchVariableData(BpelRuntimeContext brc, VariableInstance variable, boolean forWriting) |
| throws FaultException |
| { |
| if (variable.declaration.getExtVar() != null) { |
| // Note, that when using external variables, the database will not contain the value of the |
| // variable, instead we need to go the external variable subsystems. |
| Element reference = (Element) fetchVariableData(brc, resolve(variable.declaration.getExtVar().getRelated()), false); |
| try { |
| Node ret = brc.readExtVar(variable.declaration, reference ); |
| if (ret == null) { |
| throw new FaultException(oscope.getOwner().getConstants().getQnUninitializedVariable(), |
| "The external variable \"" + variable.declaration.getName() + "\" has not been initialized."); |
| } |
| return ret; |
| } catch (IncompleteKeyException ike) { |
| // This indicates that the external variable needed to be written do, put has not been. |
| __log.error("External variable could not be read due to incomplete key; the following key " + |
| "components were missing: " + ike.getMissing()); |
| throw new FaultException(oscope.getOwner().getConstants().getQnUninitializedVariable(), |
| "The extenral variable \"" + variable.declaration.getName() + "\" has not been properly initialized;" + |
| "the following key compoenents were missing:" + ike.getMissing()); |
| } catch (ExternalVariableModuleException e) { |
| throw new BpelEngineException(e); |
| } |
| } else /* not external */ { |
| Node data = brc.readVariable(variable.scopeInstance,variable.declaration.getName(), forWriting); |
| if (data == null) { |
| // Special case of messageType variables with no part |
| if (variable.declaration.getType() instanceof OMessageVarType) { |
| OMessageVarType msgType = (OMessageVarType) variable.declaration.getType(); |
| if (msgType.getParts().size() == 0) { |
| Document doc = DOMUtils.newDocument(); |
| Element root = doc.createElement("message"); |
| doc.appendChild(root); |
| return root; |
| } |
| } |
| throw new FaultException(oscope.getOwner().getConstants().getQnUninitializedVariable(), |
| "The variable " + variable.declaration.getName() + " isn't properly initialized."); |
| } |
| return data; |
| } |
| } |
| |
| |
| Node fetchVariableData(BpelRuntimeContext brc, VariableInstance var, OMessageVarType.Part part, boolean forWriting) |
| throws FaultException { |
| Node container = fetchVariableData(brc, var, forWriting); |
| |
| // If we want a specific part, we will need to navigate through the |
| // message/part structure |
| if (var.declaration.getType() instanceof OMessageVarType && part != null) { |
| container = getPartData((Element) container, part); |
| } |
| return container; |
| } |
| |
| |
| Node initializeVariable(BpelRuntimeContext context, VariableInstance var, Node value) |
| throws ExternalVariableModuleException { |
| if (var.declaration.getExtVar() != null) /* external variable */ { |
| if (__log.isDebugEnabled()) |
| __log.debug("Initialize external variable: name="+var.declaration + " value="+DOMUtils.domToString(value)); |
| VariableInstance related = resolve(var.declaration.getExtVar().getRelated()); |
| Node reference = null; |
| try { |
| reference = fetchVariableData(context, related, true); |
| } catch (FaultException fe) { |
| // In this context this is not necessarily a problem, since the assignment may re-init the related var |
| } |
| if (reference != null) value = context.readExtVar(var.declaration, reference); |
| |
| return value; |
| } else /* normal variable */ { |
| if (__log.isDebugEnabled()) __log.debug("Initialize variable: name="+var.declaration + " value="+DOMUtils.domToString(value)); |
| return context.writeVariable(var, value); |
| } |
| } |
| |
| |
| Node commitChanges(BpelRuntimeContext context, VariableInstance var, Node value) throws ExternalVariableModuleException { |
| return writeVariable(context, var, value); |
| } |
| |
| |
| Node writeVariable(BpelRuntimeContext context, VariableInstance var, Node value) throws ExternalVariableModuleException { |
| if (var.declaration.getExtVar() != null) /* external variable */ { |
| if(__log.isDebugEnabled()) |
| __log.debug("Write external variable: name="+var.declaration + " value="+DOMUtils.domToString(value)); |
| VariableInstance related = resolve(var.declaration.getExtVar().getRelated()); |
| Node reference = null; |
| try { |
| reference = fetchVariableData(context, related, true); |
| } catch (FaultException fe) { |
| // In this context this is not necessarily a problem, since the assignment may re-init the related var |
| } |
| |
| ValueReferencePair vrp = context.writeExtVar(var.declaration, reference, value); |
| writeVariable(context, related, vrp.reference); |
| return vrp.value; |
| } else /* normal variable */ { |
| if(__log.isDebugEnabled()) |
| __log.debug("Write variable: name="+var.declaration + " value="+DOMUtils.domToString(value)); |
| return context.writeVariable(var, value); |
| } |
| } |
| |
| |
| Node getPartData(Element message, Part part) { |
| // borrowed from ASSIGN.evalQuery() |
| QName partName = new QName(null, part.getName()); |
| Node ret = DOMUtils.findChildByName(message, partName); |
| if (part.getType() instanceof OElementVarType) { |
| QName elName = ((OElementVarType) part.getType()).getElementType(); |
| ret = DOMUtils.findChildByName((Element) ret, elName); |
| } |
| return ret; |
| } |
| |
| } |