Skip to content

Commit

Permalink
Fixed bug with global prism container definitions pointing to type in…
Browse files Browse the repository at this point in the history
… another schema (e.g. c:workItemActions in mext schema).
  • Loading branch information
mederly committed Feb 10, 2017
1 parent 8b294f8 commit 671a2bc
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 43 deletions.
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2010-2017 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.prism.schema;

import com.evolveum.midpoint.prism.Definition;
import com.evolveum.midpoint.util.exception.SchemaException;

/**
* @author mederly
*/
@FunctionalInterface
public interface DefinitionSupplier {
Definition get() throws SchemaException;
}
Expand Up @@ -79,6 +79,7 @@ class DomToSchemaProcessor {
private XSSchemaSet xsSchemaSet;
private String shortDescription;
private boolean isRuntime = false;
private boolean allowDelayedItemDefinitions;

public EntityResolver getEntityResolver() {
return entityResolver;
Expand Down Expand Up @@ -128,6 +129,14 @@ public void setRuntime(boolean isRuntime) {
this.isRuntime = isRuntime;
}

public boolean isAllowDelayedItemDefinitions() {
return allowDelayedItemDefinitions;
}

public void setAllowDelayedItemDefinitions(boolean allowDelayedItemDefinitions) {
this.allowDelayedItemDefinitions = allowDelayedItemDefinitions;
}

/**
* Main entry point.
*
Expand Down Expand Up @@ -673,9 +682,22 @@ private void createDefinitionsFromElements(XSSchemaSet set) throws SchemaExcepti
ItemDefinitionImpl definition;

if (isPropertyContainer(xsElementDecl) || isObjectDefinition(xsType)) {
ComplexTypeDefinition complexTypeDefinition = schema.findComplexTypeDefinition(typeQName);
definition = createPropertyContainerDefinition(
xsType, xsElementDecl, complexTypeDefinition, annotation, null, true);
ComplexTypeDefinition complexTypeDefinition = findComplexTypeDefinition(typeQName);
if (complexTypeDefinition == null) {
if (!allowDelayedItemDefinitions) {
throw new SchemaException("Couldn't parse prism container " + elementName + " of type " + typeQName
+ " because complex type definition couldn't be found and delayed item definitions are not allowed.");
}
definition = null;
schema.addDelayedItemDefinition(() -> {
ComplexTypeDefinition ctd = findComplexTypeDefinition(typeQName);
// here we take the risk that ctd is null
return createPropertyContainerDefinition(xsType, xsElementDecl, ctd, annotation, null, true);
});
} else {
definition = createPropertyContainerDefinition(
xsType, xsElementDecl, complexTypeDefinition, annotation, null, true);
}
} else if (isObjectReference(xsElementDecl, xsType)) {
definition = processObjectReferenceDefinition(xsType, elementName,
annotation, null, null, false);
Expand All @@ -684,10 +706,12 @@ private void createDefinitionsFromElements(XSSchemaSet set) throws SchemaExcepti
definition = createPropertyDefinition(xsType, elementName, typeQName,
null, annotation, null);
}
definition.setSubstitutionHead(getSubstitutionHead(xsElementDecl));
schema.getDefinitions().add(definition);
if (definition != null) {
definition.setSubstitutionHead(getSubstitutionHead(xsElementDecl));
schema.getDefinitions().add(definition);
}

} else if (xsElementDecl.getTargetNamespace().equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) {
} else { //if (xsElementDecl.getTargetNamespace().equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) {
// This is OK to ignore. These are imported elements from other
// schemas
// } else {
Expand All @@ -699,6 +723,15 @@ private void createDefinitionsFromElements(XSSchemaSet set) throws SchemaExcepti
}
}

// We first try to find the definition locally, because in schema registry we don't have the current schema yet.
private ComplexTypeDefinition findComplexTypeDefinition(QName typeQName) {
ComplexTypeDefinition complexTypeDefinition = schema.findComplexTypeDefinitionByType(typeQName);
if (complexTypeDefinition == null) {
complexTypeDefinition = getSchemaRegistry().findComplexTypeDefinitionByType(typeQName);
}
return complexTypeDefinition;
}

private QName getSubstitutionHead(XSElementDecl element) {
XSElementDecl head = element.getSubstAffiliation();
if (head == null) {
Expand Down Expand Up @@ -931,19 +964,17 @@ private PrismContainerDefinitionImpl<?> createPropertyContainerDefinition(XSType

SchemaDefinitionFactory definitionFactory = getDefinitionFactory();

Class compileTimeClass = null;
if (getSchemaRegistry() != null && complexTypeDefinition != null) {
compileTimeClass = getSchemaRegistry().determineCompileTimeClass(complexTypeDefinition.getTypeName());
}
if (isObjectDefinition(xsType)) {
Class compileTimeClass = null;
if (getSchemaRegistry() != null) {
compileTimeClass = getSchemaRegistry().determineCompileTimeClass(complexTypeDefinition.getTypeName());
}
pcd = definitionFactory.createObjectDefinition(elementName, complexTypeDefinition, prismContext,
compileTimeClass, annotation, elementParticle);
pcd = definitionFactory.createObjectDefinition(elementName, complexTypeDefinition, prismContext, compileTimeClass);
// Multiplicity is fixed to a single-value here
pcd.setMinOccurs(1);
pcd.setMaxOccurs(1);
} else {
pcd = definitionFactory.createContainerDefinition(elementName, complexTypeDefinition,
prismContext, annotation, elementParticle);
pcd = definitionFactory.createContainerDefinition(elementName, complexTypeDefinition, prismContext, compileTimeClass);
setMultiplicity(pcd, elementParticle, elementDecl.getAnnotation(), topLevel);
}

Expand Down Expand Up @@ -1131,8 +1162,6 @@ private void parseItemDefinitionAnnotations(ItemDefinitionImpl itemDef, XSAnnota
return;
}

QName elementName = itemDef.getName();

// ignore
Boolean ignore = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_IGNORE);
if (ignore != null) {
Expand Down
Expand Up @@ -16,23 +16,22 @@

package com.evolveum.midpoint.prism.schema;

import java.util.*;
import java.util.stream.Collectors;

import javax.xml.namespace.QName;

import com.evolveum.midpoint.prism.*;

import com.evolveum.midpoint.prism.xml.XsdTypeMapper;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.EntityResolver;

import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

/**
*
Expand All @@ -49,6 +48,11 @@ public class PrismSchemaImpl implements PrismSchema {
protected String namespace; // may be null if not properly initialized
protected PrismContext prismContext;

// Item definitions that couldn't be created when parsing the schema because of unresolvable CTD.
// (Caused by the fact that the type resides in another schema.)
// These definitions are to be resolved after parsing the set of schemas.
@NotNull private final List<DefinitionSupplier> delayedItemDefinitions = new ArrayList<>();

protected PrismSchemaImpl(PrismContext prismContext) {
this.prismContext = prismContext;
}
Expand Down Expand Up @@ -87,6 +91,15 @@ public <T extends Definition> List<T> getDefinitions(@NotNull Class<T> type) {
.collect(Collectors.toList());
}

void addDelayedItemDefinition(DefinitionSupplier supplier) {
delayedItemDefinitions.add(supplier);
}

@NotNull
List<DefinitionSupplier> getDelayedItemDefinitions() {
return delayedItemDefinitions;
}

@Override
public boolean isEmpty() {
return definitions.isEmpty();
Expand All @@ -105,18 +118,21 @@ public PrismContext getPrismContext() {
//region XSD parsing and serialization
// TODO: cleanup this chaos
public static PrismSchema parse(Element element, boolean isRuntime, String shortDescription, PrismContext prismContext) throws SchemaException {
return parse(element, prismContext.getEntityResolver(), new PrismSchemaImpl(prismContext), isRuntime, shortDescription, prismContext);
return parse(element, prismContext.getEntityResolver(), new PrismSchemaImpl(prismContext), isRuntime, shortDescription,
false, prismContext);
}

public static PrismSchema parse(Element element, EntityResolver resolver, boolean isRuntime, String shortDescription, PrismContext prismContext) throws SchemaException {
return parse(element, resolver, new PrismSchemaImpl(prismContext), isRuntime, shortDescription, prismContext);
public static PrismSchema parse(Element element, EntityResolver resolver, boolean isRuntime, String shortDescription,
boolean allowDelayedItemDefinitions, PrismContext prismContext) throws SchemaException {
return parse(element, resolver, new PrismSchemaImpl(prismContext), isRuntime, shortDescription, allowDelayedItemDefinitions, prismContext);
}

protected static PrismSchema parse(Element element, PrismSchemaImpl schema, boolean isRuntime, String shortDescription, PrismContext prismContext) throws SchemaException {
return parse(element, prismContext.getEntityResolver(), schema, isRuntime, shortDescription, prismContext);
return parse(element, prismContext.getEntityResolver(), schema, isRuntime, shortDescription, false, prismContext);
}

protected static PrismSchema parse(Element element, EntityResolver resolver, PrismSchemaImpl schema, boolean isRuntime, String shortDescription, PrismContext prismContext) throws SchemaException {
private static PrismSchema parse(Element element, EntityResolver resolver, PrismSchemaImpl schema, boolean isRuntime,
String shortDescription, boolean allowDelayedItemDefinitions, PrismContext prismContext) throws SchemaException {
if (element == null) {
throw new IllegalArgumentException("Schema element must not be null in "+shortDescription);
}
Expand All @@ -126,6 +142,7 @@ protected static PrismSchema parse(Element element, EntityResolver resolver, Pri
processor.setPrismContext(prismContext);
processor.setShortDescription(shortDescription);
processor.setRuntime(isRuntime);
processor.setAllowDelayedItemDefinitions(allowDelayedItemDefinitions);
processor.parseDom(schema, element);
return schema;
}
Expand Down
Expand Up @@ -64,15 +64,14 @@ public PrismReferenceDefinition createReferenceDefinition(QName primaryElementNa
return new PrismReferenceDefinitionImpl(primaryElementName, typeName, prismContext);
}

public <C extends Containerable> PrismContainerDefinitionImpl<C> createContainerDefinition(QName elementName, ComplexTypeDefinition complexTypeDefinition,
PrismContext prismContext, XSAnnotation annotation, XSParticle elementParticle) throws SchemaException {
return new PrismContainerDefinitionImpl<C>(elementName, complexTypeDefinition, prismContext);
public <C extends Containerable> PrismContainerDefinitionImpl<C> createContainerDefinition(QName elementName,
ComplexTypeDefinition complexTypeDefinition, PrismContext prismContext, Class<C> compileTimeClass) throws SchemaException {
return new PrismContainerDefinitionImpl<>(elementName, complexTypeDefinition, prismContext, compileTimeClass);
}

public <T extends Objectable> PrismObjectDefinitionImpl<T> createObjectDefinition(QName elementName,
ComplexTypeDefinition complexTypeDefinition, PrismContext prismContext, Class<T> compileTimeClass,
XSAnnotation annotation, XSParticle elementParticle) throws SchemaException {
return new PrismObjectDefinitionImpl<T>(elementName, complexTypeDefinition, prismContext, compileTimeClass );
ComplexTypeDefinition complexTypeDefinition, PrismContext prismContext, Class<T> compileTimeClass) throws SchemaException {
return new PrismObjectDefinitionImpl<>(elementName, complexTypeDefinition, prismContext, compileTimeClass);
}

/**
Expand Down
Expand Up @@ -324,14 +324,14 @@ public void loadPrismSchemasFromDirectory(File directory) throws FileNotFoundExc

public void loadPrismSchemaFile(File file) throws FileNotFoundException, SchemaException {
SchemaDescription desc = loadPrismSchemaFileDescription(file);
parsePrismSchema(desc);
parsePrismSchema(desc, false);
}

public void loadPrismSchemaResource(String resourcePath) throws SchemaException {
SchemaDescription desc = SchemaDescription.parseResource(resourcePath);
desc.setPrismSchema(true);
registerSchemaDescription(desc);
parsePrismSchema(desc);
parsePrismSchema(desc, false);
}

/**
Expand Down Expand Up @@ -377,21 +377,38 @@ private void parseJavaxSchema() throws SAXException, IOException {
private void parsePrismSchemas() throws SchemaException {
for (SchemaDescription schemaDescription : schemaDescriptions) {
if (schemaDescription.isPrismSchema()) {
parsePrismSchema(schemaDescription);
parsePrismSchema(schemaDescription, true);
}
}
applySchemaExtensions();
for (SchemaDescription schemaDescription : schemaDescriptions) {
if (schemaDescription.getSchema() != null) {
resolveMissingTypeDefinitionsInGlobalItemDefinitions((PrismSchemaImpl) schemaDescription.getSchema());
}
}
}

private void parsePrismSchema(SchemaDescription schemaDescription) throws SchemaException {

// global item definitions may refer to types that are not yet available
private void resolveMissingTypeDefinitionsInGlobalItemDefinitions(PrismSchemaImpl schema) throws SchemaException {
for (Iterator<DefinitionSupplier> iterator = schema.getDelayedItemDefinitions().iterator(); iterator.hasNext(); ) {
DefinitionSupplier definitionSupplier = iterator.next();
Definition definition = definitionSupplier.get();
if (definition != null) {
schema.add(definition);
}
iterator.remove();
}
}

private void parsePrismSchema(SchemaDescription schemaDescription, boolean allowDelayedItemDefinitions) throws SchemaException {
String namespace = schemaDescription.getNamespace();

Element domElement = schemaDescription.getDomElement();
boolean isRuntime = schemaDescription.getCompileTimeClassesPackage() == null;
// System.out.println("Parsing schema " + schemaDescription.getNamespace());
LOGGER.trace("Parsing schema {}, namespace: {}, isRuntime: {}",
schemaDescription.getSourceDescription(), namespace, isRuntime);
PrismSchema schema = PrismSchemaImpl.parse(domElement, entityResolver, isRuntime, schemaDescription.getSourceDescription(), getPrismContext());
PrismSchema schema = PrismSchemaImpl.parse(domElement, entityResolver, isRuntime,
schemaDescription.getSourceDescription(), allowDelayedItemDefinitions, getPrismContext());
if (StringUtils.isEmpty(namespace)) {
namespace = schema.getNamespace();
}
Expand Down
15 changes: 15 additions & 0 deletions infra/schema/src/test/resources/common/task-1.xml
Expand Up @@ -37,6 +37,21 @@
</q:filter>
</mext:objectQuery>
</extension>
<trigger id="1">
<timestamp>2017-02-16T23:13:34.675+01:00</timestamp>
<handlerUri>http://midpoint.evolveum.com/xml/ns/public/workflow/trigger/timed-action/handler-3</handlerUri>
<extension xmlns:mext="http://midpoint.evolveum.com/xml/ns/public/model/extension-3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="c:ExtensionType">
<mext:workItemId>20338</mext:workItemId>
<mext:workItemActions>
<complete>
<outcome>reject</outcome>
<notifyBeforeAction>P2D</notifyBeforeAction>
</complete>
</mext:workItemActions>
</extension>
</trigger>

<taskIdentifier>44444444-4444-4444-4444-000000001111</taskIdentifier>
<ownerRef oid="c0c010c0-d34d-b33f-f00d-111111111111"/>
Expand Down

0 comments on commit 671a2bc

Please sign in to comment.