Skip to content

Commit

Permalink
Fix DOM parsing thread safety
Browse files Browse the repository at this point in the history
DOM value parsers are walking across DOM trees (when looking for
namespace declarations) so their use is thread safe even when
synchronized. So they are now replaced by DomLessValueParsed that uses
extracts of relevant information do to the parsing.

Related and other changes:
- added immutability to XNode trees,
- fixed JaxbVisitable implementation in generated code,
- RefinedResourceSchemaImpl.getExistingRefinedSchema method.

(cherry picked from commit b09f619)
  • Loading branch information
mederly committed Jan 23, 2020
1 parent f903d00 commit bb3f9a3
Show file tree
Hide file tree
Showing 30 changed files with 672 additions and 726 deletions.
8 changes: 7 additions & 1 deletion build-system/pom.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!--
~ Copyright (c) 2010-2019 Evolveum and contributors
~ Copyright (c) 2010-2020 Evolveum and contributors
~
~ This work is dual-licensed under the Apache License 2.0
~ and European Union Public License. See LICENSE file for details.
Expand Down Expand Up @@ -398,6 +398,12 @@
<artifactId>commons-text</artifactId>
<version>1.8</version>
</dependency>
<!-- experimental -->
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
Expand Down
Expand Up @@ -284,6 +284,20 @@ public static RefinedResourceSchema getRefinedSchema(PrismObject<ResourceType> r
return getRefinedSchema(resource, resource.getPrismContext());
}

public static RefinedResourceSchema getExistingRefinedSchema(PrismObject<ResourceType> resource) {
Object userDataEntry = resource.getUserData(USER_DATA_KEY_REFINED_SCHEMA);
if (userDataEntry != null) {
if (userDataEntry instanceof RefinedResourceSchema) {
return (RefinedResourceSchema)userDataEntry;
} else {
throw new IllegalStateException("Expected RefinedResourceSchema under user data key "+USER_DATA_KEY_REFINED_SCHEMA+
"in "+resource+", but got "+userDataEntry.getClass());
}
} else {
return null;
}
}

/**
* Obtains refined schema for the resource.
*
Expand All @@ -296,19 +310,14 @@ public static RefinedResourceSchema getRefinedSchema(PrismObject<ResourceType> r
throw new SchemaException("Could not get refined schema, resource does not exist.");
}

Object userDataEntry = resource.getUserData(USER_DATA_KEY_REFINED_SCHEMA);
if (userDataEntry != null) {
if (userDataEntry instanceof RefinedResourceSchema) {
return (RefinedResourceSchema)userDataEntry;
} else {
throw new IllegalStateException("Expected RefinedResourceSchema under user data key "+USER_DATA_KEY_REFINED_SCHEMA+
"in "+resource+", but got "+userDataEntry.getClass());
}
RefinedResourceSchema existingRefinedSchema = getExistingRefinedSchema(resource);
if (existingRefinedSchema != null) {
return existingRefinedSchema;
} else {
RefinedResourceSchema refinedSchema = parse(resource, prismContext);
if (resource.isImmutable()) {
throw new IllegalStateException("Trying to set parsed schema on immutable resource: " + resource);
}
RefinedResourceSchema refinedSchema = parse(resource, prismContext);
resource.setUserData(USER_DATA_KEY_REFINED_SCHEMA, refinedSchema);
return refinedSchema;
}
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2017 Evolveum and contributors
* Copyright (c) 2010-2020 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
Expand All @@ -17,7 +17,7 @@
/**
* Contains the expression that can be part of e.g. prism filters (or other data).
*/
public class ExpressionWrapper implements Cloneable, Serializable {
public class ExpressionWrapper implements Cloneable, Serializable, Freezable {
private static final long serialVersionUID = 1L;

/**
Expand Down Expand Up @@ -56,5 +56,10 @@ public String toString() {
return "ExpressionWrapper(" + PrettyPrinter.prettyPrint(elementName) + ":" + PrettyPrinter.prettyPrint(expression);
}


@Override
public void setImmutable() {
if (expression instanceof Freezable) {
((Freezable) expression).setImmutable();
}
}
}
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2020 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.prism;

/**
* Something that can be made immutable.
*/
public interface Freezable {

void setImmutable();
}
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2020 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.prism;

/**
* Experimental.
*/
public class ImmutableUtil {

public static void throwImmutable(Object object) {
throw new IllegalStateException("Object " + object + " couldn't be modified as it is immutable");
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2018 Evolveum and contributors
* Copyright (c) 2010-2020 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
Expand Down Expand Up @@ -95,8 +95,9 @@ public interface PrismPropertyValue<T> extends DebugDumpable, Serializable, Pris
@Override
Class<?> getRealClass();

@SuppressWarnings("unchecked")
@Nullable
@Override
<T> T getRealValue();
T getRealValue();

}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2018 Evolveum and contributors
* Copyright (c) 2010-2020 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
Expand All @@ -24,7 +24,7 @@
* @author semancik
*
*/
public interface PrismValue extends Visitable, PathVisitable, Serializable, DebugDumpable, Revivable { // todo ShortDumpable?
public interface PrismValue extends Visitable, PathVisitable, Serializable, DebugDumpable, Revivable, Freezable { // todo ShortDumpable?

void setPrismContext(PrismContext prismContext);

Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2018 Evolveum and contributors
* Copyright (c) 2010-2020 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
Expand Down Expand Up @@ -28,6 +28,7 @@
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Map;

/**
* Simple implementation that converts XSD primitive types to Java (and vice
Expand Down Expand Up @@ -60,14 +61,14 @@ private static DatatypeFactory getDatatypeFactory() {
return datatypeFactory;
}

@Deprecated // do NOT use from the outside of prism
// do NOT use from the outside of prism
public static boolean canConvert(Class<?> clazz) {
return (XsdTypeMapper.getJavaToXsdMapping(clazz) != null);
return XsdTypeMapper.getJavaToXsdMapping(clazz) != null;
}

@Deprecated // do NOT use from the outside of prism
// do NOT use from the outside of prism
public static boolean canConvert(QName xsdType) {
return (XsdTypeMapper.getXsdToJavaMapping(xsdType) != null);
return XsdTypeMapper.getXsdToJavaMapping(xsdType) != null;
}

// TODO consider moving this to better place
Expand Down Expand Up @@ -368,6 +369,10 @@ public static <T> T toJavaValue(String stringContent, QName typeQName) {
return toJavaValue(stringContent, javaClass, false);
}

public static <T> T toJavaValue(String stringContent, Map<String, String> namespaces, QName typeQName) {
return toJavaValue(stringContent, namespaces, XsdTypeMapper.getXsdToJavaMapping(typeQName));
}

public static <T> T toJavaValue(Element xmlElement, Class<T> type) throws SchemaException {
if (type.equals(Element.class)) {
return (T) xmlElement;
Expand All @@ -376,16 +381,31 @@ public static <T> T toJavaValue(Element xmlElement, Class<T> type) throws Schema
} else if (PolyString.class.isAssignableFrom(type)) {
return (T) polyStringToJava(xmlElement);
} else {
String stringContent = xmlElement.getTextContent();
if (stringContent == null) {
return null;
}
T javaValue = toJavaValue(stringContent, type);
if (javaValue == null) {
throw new IllegalArgumentException("Unknown type for conversion: " + type + "(element " + DOMUtil.getQName(xmlElement) + ")");
}
return javaValue;
return toJavaValuePlain(xmlElement.getTextContent(), type, " (element " + DOMUtil.getQName(xmlElement) + ")");
}
}

public static <T> T toJavaValue(String textContent, Map<String, String> namespaces, Class<T> type) {
if (type.equals(Element.class)) {
throw new UnsupportedOperationException("Cannot convert text to Element: " + textContent);
} else if (type.equals(QName.class)) {
return (T) DOMUtil.getQNameValue(textContent, namespaces);
} else if (PolyString.class.isAssignableFrom(type)) {
return (T) new PolyString(textContent);
} else {
return toJavaValuePlain(textContent, type, "");
}
}

private static <T> T toJavaValuePlain(String textContent, Class<T> type, String context) {
if (textContent == null) {
return null;
}
T javaValue = toJavaValue(textContent, type);
if (javaValue == null) {
throw new IllegalArgumentException("Unknown type for conversion: " + type + context);
}
return javaValue;
}

private static <T> T toJavaValue(String stringContent, Class<T> type) {
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2018 Evolveum and contributors
* Copyright (c) 2010-2020 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
Expand All @@ -12,7 +12,7 @@
/**
*
*/
public interface ListXNode extends XNode{
public interface ListXNode extends XNode {
boolean isEmpty();
int size();
XNode get(int i);
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 Evolveum and contributors
* Copyright (c) 2014-2020 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
Expand Down Expand Up @@ -44,4 +44,9 @@ public interface ValueParser<T> {
* @return May return null if not supported or no namespace declarations are present.
*/
Map<String,String> getPotentiallyRelevantNamespaces();

/**
* @return Frozen version of this parser.
*/
ValueParser<T> freeze();
}
@@ -1,12 +1,13 @@
/*
* Copyright (c) 2010-2018 Evolveum and contributors
* Copyright (c) 2010-2020 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.prism.xnode;

import com.evolveum.midpoint.prism.Freezable;
import com.evolveum.midpoint.prism.Visitable;
import com.evolveum.midpoint.util.DebugDumpable;
import org.jetbrains.annotations.NotNull;
Expand All @@ -17,7 +18,7 @@
/**
*
*/
public interface XNode extends DebugDumpable, Visitable, Cloneable, Serializable {
public interface XNode extends DebugDumpable, Visitable, Cloneable, Serializable, Freezable {

boolean isEmpty();

Expand Down

0 comments on commit bb3f9a3

Please sign in to comment.