/* * Copyright (c) 2010-2013 Evolveum * * Licensed 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 com.evolveum.midpoint.model.common.expression.script.xpath; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.xml.namespace.QName; import javax.xml.xpath.XPathVariableResolver; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.evolveum.midpoint.model.common.expression.ExpressionSyntaxException; import com.evolveum.midpoint.model.common.expression.ExpressionVariables; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.Itemable; import com.evolveum.midpoint.prism.Objectable; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismProperty; import com.evolveum.midpoint.prism.PrismPropertyValue; import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ObjectResolver; import com.evolveum.midpoint.util.DebugDumpable; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.TunnelException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; /** * XPath variable resolver that stores variables in the map and supports lazy * resolution of objects. * * @author Igor Farinic * @author Radovan Semancik */ public class LazyXPathVariableResolver implements XPathVariableResolver { private static final QName FAKE_VARIABLE_QNAME = new QName(SchemaConstants.NS_C, "fakeVar"); private ExpressionVariables variables; private ObjectResolver objectResolver; private String contextDescription; private OperationResult result; private PrismContext prismContext; private static final Trace LOGGER = TraceManager.getTrace(LazyXPathVariableResolver.class); public LazyXPathVariableResolver(ExpressionVariables variables, ObjectResolver objectResolver, String contextDescription, PrismContext prismContext, OperationResult result) { this.variables = variables; this.objectResolver = objectResolver; this.contextDescription = contextDescription; this.result = result; this.prismContext = prismContext; } @Override public Object resolveVariable(QName name) { if (variables == null) { return null; } if (name != null && (name.getNamespaceURI() == null || name.getNamespaceURI().isEmpty())) { LOGGER.warn("Using variable without a namespace ("+name+"), possible namespace problem (e.g. missing namespace prefix declaration) in "+contextDescription); } // Note: null is a legal variable name here. It corresponds to the root node Object variableValue = variables.get(name); if (variableValue == null) { // TODO: warning ??? return null; } QName type = null; // Attempt to resolve object reference if (objectResolver != null && variableValue instanceof ObjectReferenceType) { ObjectReferenceType ref = (ObjectReferenceType)variableValue; if (ref.getOid() == null) { SchemaException newEx = new SchemaException("Null OID in reference in variable "+name+" in "+contextDescription, name); throw new TunnelException(newEx); } else { type = ref.getType(); try { variableValue = objectResolver.resolve(ref, ObjectType.class, null, contextDescription, null, result); // TODO task } catch (ObjectNotFoundException e) { ObjectNotFoundException newEx = new ObjectNotFoundException("Object not found during variable "+name+" resolution in "+contextDescription+": "+e.getMessage(),e, ref.getOid()); // We have no other practical way how to handle the error throw new TunnelException(newEx); } catch (SchemaException e) { ExpressionSyntaxException newEx = new ExpressionSyntaxException("Schema error during variable "+name+" resolution in "+contextDescription+": "+e.getMessage(), e, name); throw new TunnelException(newEx); } } } try { return convertToXml(variableValue, name, prismContext, contextDescription); } catch (SchemaException e) { throw new TunnelException(e); } } // May return primitive types or DOM Node public static Object convertToXml(Object variableValue, QName variableName, final PrismContext prismContext, String contextDescription) throws SchemaException { try { if (variableValue instanceof Objectable) { variableValue = ((Objectable)variableValue).asPrismObject(); } if (variableValue instanceof PrismObject) { PrismObject<?> prismObject = (PrismObject<?>)variableValue; variableValue = prismObject.getPrismContext().domSerializer().serialize(prismObject); } else if (variableValue instanceof PrismProperty<?>) { PrismProperty<?> prismProperty = (PrismProperty<?>)variableValue; final List<Element> elementList = new ArrayList<Element>(); for (PrismPropertyValue<?> value: prismProperty.getValues()) { Element valueElement = prismContext.domSerializer().serialize(value, prismProperty.getElementName()); elementList.add(valueElement); } NodeList nodeList = new AdHocNodeList(elementList); variableValue = nodeList; } else if (variableValue instanceof PrismValue) { PrismValue pval = (PrismValue)variableValue; if (pval.getParent() == null) { // Set a fake parent to allow serialization pval.setParent(new AdHocItemable(prismContext)); } variableValue = prismContext.domSerializer().serialize(pval, variableName); } if (!((variableValue instanceof Node)||variableValue instanceof NodeList) && !(variableValue.getClass().getPackage().getName().startsWith("java."))) { throw new SchemaException("Unable to convert value of variable "+variableName+" to XML, still got "+variableValue.getClass().getName()+":"+variableValue+" value at the end"); } // DEBUG hack // if (LOGGER.isDebugEnabled()) { // LOGGER.trace("VAR "+variableName+" - "+variableValue.getClass().getName()+":"); // if (variableValue instanceof Node) { // LOGGER.trace(DOMUtil.serializeDOMToString((Node)variableValue)); // } else { // LOGGER.trace(PrettyPrinter.prettyPrint(variableValue)); // } // } return variableValue; } catch (SchemaException e) { if (variableValue != null && variableValue instanceof DebugDumpable) { LOGGER.trace("Value of variable {}:\n{}", variableName, ((DebugDumpable)variableValue).debugDump()); } throw new SchemaException(e.getMessage() + " while processing variable "+variableName+" with value "+variableValue +" in "+contextDescription, e); } catch (RuntimeException e) { if (variableValue != null && variableValue instanceof DebugDumpable) { LOGGER.trace("Value of variable {}:\n{}", variableName, ((DebugDumpable)variableValue).debugDump()); } throw new RuntimeException(e.getClass().getName()+ ": "+e.getMessage() + " while processing variable "+variableName +" with value "+variableValue+" in "+contextDescription, e); } } private static class AdHocNodeList implements NodeList, Serializable { private final List<Element> elementList; public AdHocNodeList(List<Element> elementList) { this.elementList = elementList; } @Override public Node item(int index) { return elementList.get(index); } @Override public int getLength() { return elementList.size(); } } private static class AdHocItemable implements Itemable, Serializable { private transient final PrismContext prismContext; // might be a problem ... but XPath is not supported anyway public AdHocItemable(PrismContext prismContext) { this.prismContext = prismContext; } @Override public PrismContext getPrismContext() { return prismContext; } @Override public ItemPath getPath() { return null; } @Override public QName getElementName() { return FAKE_VARIABLE_QNAME; } @Override public ItemDefinition getDefinition() { return null; } } }