diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/SchrodingerComponentInitListener.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/SchrodingerComponentInitListener.java index 3294754e4e9..0f4c1ffe42a 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/SchrodingerComponentInitListener.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/util/SchrodingerComponentInitListener.java @@ -9,6 +9,8 @@ import java.io.Serializable; import javax.xml.namespace.QName; +import com.evolveum.midpoint.gui.api.component.captcha.CaptchaPanel; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.wicket.AttributeModifier; @@ -52,7 +54,11 @@ public void onInitialize(Component component) { } private void handleId(Component component) { - writeDataAttribute(component, ATTR_ID, component.getId()); + if (component instanceof CaptchaPanel) { + writeDataAttribute(component, ATTR_ID, component.getId() + ((CaptchaPanel) component).getRandomText()); + } else { + writeDataAttribute(component, ATTR_ID, component.getId()); + } } private void writeDataAttribute(Component component, String key, String value) { diff --git a/gui/admin-gui/src/test/java/com/evolveum/midpoint/gui/MidScaleGuiTest.java b/gui/admin-gui/src/test/java/com/evolveum/midpoint/gui/MidScaleGuiTest.java index 4e0162d1d86..00d2a183076 100644 --- a/gui/admin-gui/src/test/java/com/evolveum/midpoint/gui/MidScaleGuiTest.java +++ b/gui/admin-gui/src/test/java/com/evolveum/midpoint/gui/MidScaleGuiTest.java @@ -9,9 +9,13 @@ import java.io.File; import com.evolveum.midpoint.gui.api.component.MainObjectListPanel; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.web.AbstractGuiIntegrationTest; import com.evolveum.midpoint.web.page.admin.configuration.PageSystemConfiguration; import com.evolveum.midpoint.web.page.admin.server.PageTasks; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + import org.apache.wicket.request.mapper.parameter.PageParameters; import org.javasimon.Split; import org.javasimon.Stopwatch; @@ -39,33 +43,34 @@ import com.evolveum.midpoint.web.page.self.PageSelfCredentials; import com.evolveum.midpoint.web.page.self.PageSelfDashboard; import com.evolveum.midpoint.web.page.self.PageUserSelfProfile; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AdminGuiConfigurationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationsPerformanceInformationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType; -@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +//@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) @ActiveProfiles("test") @SpringBootTest(classes = TestMidPointSpringApplication.class) -public class MidScaleGuiTest extends AbstractInitializedGuiIntegrationTest implements PerformanceTestMethodMixin { +public class MidScaleGuiTest extends AbstractGuiIntegrationTest implements PerformanceTestMethodMixin { private static final String TEST_DIR = "./src/test/resources/midScale"; - private static final File FILE_ORG_STRUCT = new File(TEST_DIR, "org-struct.xml"); - private static final File FILE_USERS = new File(TEST_DIR, "users.xml"); +// private static final File FILE_ORG_STRUCT = new File(TEST_DIR, "org-struct.xml"); +// private static final File FILE_USERS = new File(TEST_DIR, "users.xml"); private static final int REPETITION_COUNT = 10; + protected PrismObject userAdministrator; + @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); - - importObjectsFromFileNotRaw(FILE_ORG_STRUCT, initTask, initResult); - initResult.computeStatusIfUnknown(); - if (!initResult.isSuccess()) { - System.out.println("init result:\n" + initResult); - } - importObjectsFromFileNotRaw(FILE_USERS, initTask, initResult); + modelService.postInit(initResult); + userAdministrator = repositoryService.getObject(UserType.class, USER_ADMINISTRATOR_OID, null, initResult); + login(userAdministrator); + +// importObjectsFromFileNotRaw(FILE_ORG_STRUCT, initTask, initResult); +// initResult.computeStatusIfUnknown(); +// if (!initResult.isSuccess()) { +// System.out.println("init result:\n" + initResult); +// } +// importObjectsFromFileNotRaw(FILE_USERS, initTask, initResult); modifyObjectReplaceProperty(SystemConfigurationType.class, SystemObjectsType.SYSTEM_CONFIGURATION.value(), ItemPath.create(SystemConfigurationType.F_ADMIN_GUI_CONFIGURATION, AdminGuiConfigurationType.F_ENABLE_EXPERIMENTAL_FEATURES), diff --git a/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyObject.java b/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyObject.java index 30bd7b0704c..1f5f2d60ef8 100644 --- a/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyObject.java +++ b/icf-connectors/dummy-resource/src/main/java/com/evolveum/icf/dummy/resource/DummyObject.java @@ -8,16 +8,8 @@ import java.io.FileNotFoundException; import java.net.ConnectException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang.StringUtils; @@ -25,23 +17,26 @@ import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.PrettyPrinter; +import static java.util.Collections.*; + /** - * @author Radovan Semancik + * TODO Treat null attribute values more consistently. * + * @author Radovan Semancik */ public abstract class DummyObject implements DebugDumpable { private String id; // private int internalId = -1; private String name; - private Map> attributes = new HashMap<>(); + private final Map> attributes = new ConcurrentHashMap<>(); private Boolean enabled = true; private Date validFrom = null; private Date validTo = null; private String lastModifier; protected DummyResource resource; - private final Set auxiliaryObjectClassNames = new HashSet<>(); + private final Set auxiliaryObjectClassNames = ConcurrentHashMap.newKeySet(); private BreakMode modifyBreakMode = null; @@ -80,33 +75,36 @@ public Boolean isEnabled() { return enabled; } - public void setEnabled(Boolean enabled) throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + public void setEnabled(Boolean enabled) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { checkModifyBreak(); delayOperation(); this.enabled = enabled; - recordModify("_ENABLED", null, null, Arrays.asList(enabled)); + recordModify("_ENABLED", null, null, singletonList(enabled)); } public Date getValidFrom() { return validFrom; } - public void setValidFrom(Date validFrom) throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + public void setValidFrom(Date validFrom) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { checkModifyBreak(); delayOperation(); this.validFrom = validFrom; - recordModify("_VALID_FROM", null, null, Arrays.asList(validFrom)); + recordModify("_VALID_FROM", null, null, singletonList(validFrom)); } public Date getValidTo() { return validTo; } - public void setValidTo(Date validTo) throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + public void setValidTo(Date validTo) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { checkModifyBreak(); delayOperation(); this.validTo = validTo; - recordModify("_VALID_TO", null, null, Arrays.asList(validTo)); + recordModify("_VALID_TO", null, null, singletonList(validTo)); } public String getLastModifier() { @@ -130,8 +128,8 @@ public Set getAttributeNames() { } public Set getAttributeValues(String attrName, Class type) { - Set values = attributes.get(attrName); - return (Set)values; + //noinspection unchecked + return (Set) attributes.get(attrName); } public T getAttributeValue(String attrName, Class type) { @@ -139,8 +137,8 @@ public T getAttributeValue(String attrName, Class type) { if (values == null || values.isEmpty()) { return null; } - if (values.size()>1) { - throw new IllegalArgumentException("Attempt to fetch single value from a multi-valued attribute "+attrName); + if (values.size() > 1) { + throw new IllegalArgumentException("Attempt to fetch single value from a multi-valued attribute " + attrName); } return values.iterator().next(); } @@ -149,83 +147,95 @@ public String getAttributeValue(String attrName) { return getAttributeValue(attrName,String.class); } - public void replaceAttributeValue(String name, Object value) throws SchemaViolationException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { - Collection values = new ArrayList<>(1); - values.add(value); - replaceAttributeValues(name, values); + public void replaceAttributeValue(String name, Object value) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + replaceAttributeValues(name, createCollection(value)); } - public void replaceAttributeValues(String name, Collection values) throws SchemaViolationException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + private Set createCollection(Object value) { + return value != null ? singleton(value) : emptySet(); + } + + public void replaceAttributeValues(String name, Collection values) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { checkModifyBreak(); delayOperation(); - Set currentValues = attributes.get(name); - if (currentValues == null) { - currentValues = new HashSet<>(); - attributes.put(name, currentValues); - } else { - currentValues.clear(); - } - currentValues.addAll(values); + + Set currentValues = getOrCreateAttributeValueSet(name); + currentValues.clear(); + + addNonNullValues(currentValues, values); checkSchema(name, values, "replace"); recordModify(name, null, null, values); } - public void replaceAttributeValues(String name, Object... values) throws SchemaViolationException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + private void addNonNullValues(Set currentValues, Collection values) { + for (Object value : values) { + if (value != null) { + currentValues.add(value); + } + } + } + + private Set getOrCreateAttributeValueSet(String name) { + return attributes.computeIfAbsent(name, k -> ConcurrentHashMap.newKeySet()); + } + + public void replaceAttributeValues(String name, Object... values) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { checkModifyBreak(); delayOperation(); - Set currentValues = attributes.get(name); - if (currentValues == null) { - currentValues = new HashSet<>(); - attributes.put(name, currentValues); - } else { - currentValues.clear(); - } + + Set currentValues = getOrCreateAttributeValueSet(name); + currentValues.clear(); + List valuesList = Arrays.asList(values); - currentValues.addAll(valuesList); + addNonNullValues(currentValues, valuesList); checkSchema(name, valuesList, "replace"); + + // Why isn't the same code in the "collection" version of this method (above)? + // Maybe to check null-attribute handling of UCF layer, see TestDummy.test129NullAttributeValue. if (valuesList.isEmpty()) { attributes.remove(name); } - recordModify(name, null, null, Arrays.asList(values)); + recordModify(name, null, null, valuesList); } - public void addAttributeValue(String name, Object value) throws SchemaViolationException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { - Collection values = new ArrayList<>(1); - values.add(value); - addAttributeValues(name, values); + public void addAttributeValue(String name, Object value) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + addAttributeValues(name, createCollection(value)); } - public void addAttributeValues(String name, Collection valuesToAdd) throws SchemaViolationException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + public void addAttributeValues(String name, Collection valuesToAdd) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { checkModifyBreak(); delayOperation(); - Set currentValues = attributes.get(name); - if (currentValues == null) { - currentValues = new HashSet<>(); - attributes.put(name, currentValues); - } - for(T valueToAdd: valuesToAdd) { + Set currentValues = getOrCreateAttributeValueSet(name); + for (T valueToAdd: valuesToAdd) { addAttributeValue(name, currentValues, valueToAdd); } recordModify(name, valuesToAdd, null, null); } - public void addAttributeValues(String name, String... valuesToAdd) throws SchemaViolationException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + public void addAttributeValues(String name, String... valuesToAdd) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { checkModifyBreak(); delayOperation(); - Set currentValues = attributes.get(name); - if (currentValues == null) { - currentValues = new HashSet<>(); - attributes.put(name, currentValues); - } + Set currentValues = getOrCreateAttributeValueSet(name); for (Object valueToAdd: valuesToAdd) { addAttributeValue(name, currentValues, valueToAdd); } recordModify(name, Arrays.asList(valuesToAdd), null, null); } - private void addAttributeValue(String attrName, Set currentValues, Object valueToAdd) throws SchemaViolationException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + private void addAttributeValue(String attrName, Set currentValues, Object valueToAdd) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { checkModifyBreak(); delayOperation(); + + if (valueToAdd == null) { + return; // Concurrent hash map does not allow null values. + } if (resource != null && !resource.isTolerateDuplicateValues()) { for (Object currentValue: currentValues) { if (currentValue.equals(valueToAdd)) { @@ -257,34 +267,29 @@ private void addAttributeValue(String attrName, Set currentValues, Objec currentValues.add(valueToAdd); } - public void removeAttributeValue(String name, Object value) throws SchemaViolationException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { - Collection values = new ArrayList<>(); - values.add(value); - removeAttributeValues(name, values); + public void removeAttributeValue(String name, Object value) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + removeAttributeValues(name, createCollection(value)); } - public void removeAttributeValues(String name, Collection values) throws SchemaViolationException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { + public void removeAttributeValues(String name, Collection values) + throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException, InterruptedException { checkModifyBreak(); delayOperation(); - Set currentValues = attributes.get(name); - if (currentValues == null) { - currentValues = new HashSet<>(); - attributes.put(name, currentValues); - } + Set currentValues = getOrCreateAttributeValueSet(name); - Set valuesToCheck = new HashSet<>(); - valuesToCheck.addAll(currentValues); + Set valuesToCheck = new HashSet<>(currentValues); valuesToCheck.removeAll(values); checkSchema(name, valuesToCheck, "remove"); Iterator iterator = currentValues.iterator(); boolean foundMember = false; - if (name.equals(DummyGroup.ATTR_MEMBERS_NAME) && !resource.isTolerateDuplicateValues()){ + if (name.equals(DummyGroup.ATTR_MEMBERS_NAME) && !resource.isTolerateDuplicateValues()) { checkIfExist(values, currentValues); } - while(iterator.hasNext()) { + while (iterator.hasNext()) { Object currentValue = iterator.next(); boolean found = false; for (Object value: values) { @@ -351,7 +356,7 @@ private void checkIfExist(Collection valuesToDelete, Set currentV } } - if (!found){ + if (!found) { throw new SchemaViolationException("no such member: " + valueToDelete + " in " + currentValues); } } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/concepts/AbstractLazy.java b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/AbstractLazy.java index f901fdac12b..bd852ee2fce 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/concepts/AbstractLazy.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/AbstractLazy.java @@ -8,20 +8,21 @@ public abstract class AbstractLazy { - private Object value; + private volatile Object value; AbstractLazy(Object supplier) { value = supplier; } T unwrap() { - if (value instanceof Lazy.Supplier) { + Object val = this.value; + if (val instanceof Lazy.Supplier) { //noinspection unchecked - value = ((Lazy.Supplier) value).get(); + this.value = ((Lazy.Supplier) val).get(); return unwrap(); } //noinspection unchecked - return (T) value; + return (T) val; } @Override diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/AbstractStatisticsPrinter.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/AbstractStatisticsPrinter.java index 840d45549c8..1e983bf7aef 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/AbstractStatisticsPrinter.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/AbstractStatisticsPrinter.java @@ -9,6 +9,8 @@ import org.jetbrains.annotations.NotNull; +import java.util.function.Supplier; + import static com.evolveum.midpoint.util.MiscUtil.or0; /** @@ -17,7 +19,20 @@ public abstract class AbstractStatisticsPrinter { public enum Format { - TEXT, CSV + + TEXT(AsciiTableFormatting::new), + CSV(CsvFormatting::new), + RAW(RawFormatting::new); + + @NotNull private final Supplier formattingSupplier; + + Format(@NotNull Supplier formattingSupplier) { + this.formattingSupplier = formattingSupplier; + } + + Formatting createFormatting() { + return formattingSupplier.get(); + } } public enum SortBy { @@ -60,7 +75,7 @@ public AbstractStatisticsPrinter(@NotNull T information, Options options, Intege this.seconds = seconds; } - protected void initData() { + void initData() { if (data == null) { data = new Data(); } else { @@ -68,23 +83,22 @@ protected void initData() { } } - protected void initFormatting() { + void initFormatting() { if (formatting == null) { - if (isCsv()) { - formatting = new CsvFormatting(); - } else { - formatting = new AsciiTableFormatting(); - } + formatting = options.format.createFormatting(); } else { throw new IllegalStateException("formatting already created"); } } - protected boolean isCsv() { - return options.format == Format.CSV; + public String print() { + prepare(); + return applyFormatting(); } - protected String applyFormatting() { + public abstract void prepare(); + + String applyFormatting() { return formatting.apply(data); } @@ -123,21 +137,21 @@ long zeroIfNull(Long n) { return n != null ? n : 0; } - protected X nullIfFalse(boolean condition, X value) { + X nullIfFalse(boolean condition, X value) { return condition ? value : null; } public static class Options { - final Format format; - final SortBy sortBy; + @NotNull final Format format; + @NotNull final SortBy sortBy; public Options() { this(null, null); } public Options(Format format, SortBy sortBy) { - this.format = format; - this.sortBy = sortBy; + this.format = format != null ? format : Format.TEXT; + this.sortBy = sortBy != null ? sortBy : SortBy.NAME; } } @@ -146,18 +160,26 @@ String formatString() { } String formatInt() { - return isCsv() ? "%d" : "%,d"; + return formatting.isNiceNumbersFormatting() ? "%,d" : "%d"; } String formatFloat1() { - return isCsv() ? "%f" : "%,.1f"; + return formatting.isNiceNumbersFormatting() ? "%,.1f" : "%f"; } String formatPercent1() { - return isCsv() ? "%f%%" : "%.1f%%"; + return formatting.isNiceNumbersFormatting() ? "%.1f%%" : "%f%%"; } String formatPercent2() { - return isCsv() ? "%f%%" : "%.2f%%"; + return formatting.isNiceNumbersFormatting() ? "%.2f%%" : "%f%%"; + } + + public Data getData() { + return data; + } + + public Formatting getFormatting() { + return formatting; } } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/AsciiTableFormatting.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/AsciiTableFormatting.java index 26f7ada5588..26ad8629926 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/AsciiTableFormatting.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/AsciiTableFormatting.java @@ -98,4 +98,9 @@ private String getTableAsString() { } return sb.toString(); } + + @Override + public boolean isNiceNumbersFormatting() { + return true; + } } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/CachePerformanceInformationPrinter.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/CachePerformanceInformationPrinter.java index 9248b35fd50..2de89ec7f1f 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/CachePerformanceInformationPrinter.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/CachePerformanceInformationPrinter.java @@ -28,11 +28,10 @@ public CachePerformanceInformationPrinter(@NotNull CachesPerformanceInformationT super(information, options, null, null); } - public String print() { + public void prepare() { List caches = getSortedCaches(); createData(caches); createFormatting(); - return applyFormatting(); } @NotNull diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/Data.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/Data.java index dc8c833cc41..3e67e0f70d5 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/Data.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/Data.java @@ -7,16 +7,17 @@ package com.evolveum.midpoint.schema.statistics; -import com.evolveum.midpoint.util.annotation.Experimental; - import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; + +import com.evolveum.midpoint.util.annotation.Experimental; /** * Data to be displayed. This is an abstract form suitable for outputting in various formats (ascii, csv, ...). */ @Experimental -class Data { +public class Data { private final List records = new ArrayList<>(); @@ -34,6 +35,11 @@ public List getRecords() { return records; } + public Stream getRawDataStream() { + return records.stream() + .map(record -> record.values.toArray()); + } + static class Record { private final List values = new ArrayList<>(); diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/Formatting.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/Formatting.java index d8d0921c214..443710f6477 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/Formatting.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/Formatting.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.stream.Collectors; /** * Describes formatting at abstract level. @@ -25,6 +26,12 @@ public abstract class Formatting { final List columns = new ArrayList<>(); + public List getColumnLabels() { + return columns.stream() + .map(column -> column.label) + .collect(Collectors.toList()); + } + static class Column { final String label; final Alignment alignment; @@ -63,5 +70,9 @@ enum Alignment { LEFT, RIGHT } + public boolean isNiceNumbersFormatting() { + return false; + } + public abstract String apply(Data data); } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/IterativeTaskInformationPrinter.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/IterativeTaskInformationPrinter.java index 20d64109102..b4dc3f57273 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/IterativeTaskInformationPrinter.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/IterativeTaskInformationPrinter.java @@ -28,11 +28,10 @@ public IterativeTaskInformationPrinter(@NotNull IterativeTaskInformationType inf super(information, options, null, null); } - public String print() { + @Override + public void prepare() { createData(); createFormatting(); - - return applyFormatting() + "\n"; } private void createData() { diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/OperationsPerformanceInformationPrinter.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/OperationsPerformanceInformationPrinter.java index 506f7174b5c..5a3224c25e5 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/OperationsPerformanceInformationPrinter.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/OperationsPerformanceInformationPrinter.java @@ -28,6 +28,8 @@ public class OperationsPerformanceInformationPrinter extends AbstractStatisticsP private final boolean viaAspect; + private List operations; + public OperationsPerformanceInformationPrinter(@NotNull OperationsPerformanceInformationType information, Options options, Integer iterations, Integer seconds, boolean viaAspect) { super(information, options, iterations, seconds); @@ -37,17 +39,22 @@ public OperationsPerformanceInformationPrinter(@NotNull OperationsPerformanceInf /** * @param nullIfNone If there are no data, returns empty string. */ - public String print(boolean nullIfNone) { - List operations = getSortedOperations(); + String print(boolean nullIfNone) { + prepare(); if (operations.isEmpty() && nullIfNone) { return null; } else { - createData(operations); - createFormatting(); return applyFormatting(); } } + @Override + public void prepare() { + operations = getSortedOperations(); + createData(operations); + createFormatting(); + } + @NotNull private List getSortedOperations() { return information.getOperation().stream() diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/ProvisioningStatisticsPrinter.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/ProvisioningStatisticsPrinter.java index cedf92dd2a7..3bed0fc3ade 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/ProvisioningStatisticsPrinter.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/ProvisioningStatisticsPrinter.java @@ -24,11 +24,10 @@ public ProvisioningStatisticsPrinter(ProvisioningStatisticsType information, Opt super(information, options, null, null); } - public String print() { + @Override + public void prepare() { createData(); createFormatting(); - - return applyFormatting() + "\n"; } private void createData() { diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/RawFormatting.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/RawFormatting.java new file mode 100644 index 00000000000..1503b4237e9 --- /dev/null +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/RawFormatting.java @@ -0,0 +1,23 @@ +/* + * 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.schema.statistics; + +import com.evolveum.midpoint.util.annotation.Experimental; + +/** + * Formatting that - in fact - does nothing. + * + * It just provides abstract Formatting functionality, namely maintains a list of column names. + */ +@Experimental +public class RawFormatting extends Formatting { + + public String apply(Data data) { + return "Raw formatting has no String representation"; + } +} diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/RepositoryPerformanceInformationPrinter.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/RepositoryPerformanceInformationPrinter.java index 09a0c82c7cc..4c2b2427c1d 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/RepositoryPerformanceInformationPrinter.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/RepositoryPerformanceInformationPrinter.java @@ -29,11 +29,11 @@ public RepositoryPerformanceInformationPrinter(@NotNull RepositoryPerformanceInf super(information, options, iterations, seconds); } - public String print() { + @Override + public void prepare() { List operations = getSortedOperations(); createData(operations); createFormatting(); - return applyFormatting(); } @NotNull diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/StructuredTaskProgressPrinter.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/StructuredTaskProgressPrinter.java index e0021e81bcb..66dcd3edd5b 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/StructuredTaskProgressPrinter.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/statistics/StructuredTaskProgressPrinter.java @@ -27,13 +27,17 @@ public class StructuredTaskProgressPrinter extends AbstractStatisticsPrinter operations = getSortedOperations(); createData(operations); createFormatting(); - return applyFormatting(); } @NotNull diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd index 9cc3fea2a46..1abd0083f84 100755 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd @@ -141,7 +141,7 @@ that each object must have to live in our system (identifier, name).

- All objects are identified by OID. The OID is an immutable identifier + All objects are identified by OID. The OID is an immutable identifier (usually UUID). Except the OID all the objects have human-readable name. The name is usually unique for each object type, but this is not a strict requirement. @@ -7486,7 +7486,7 @@ When set to true, midPoint will try to avoid adding attribute values that are already there and remove values that are not there. Some resources do not tolerate such operations and they respond with errors. However midPoint cannot rely on transactions. MidPoint's - lock-free relativistic model provides the necessary consistency, occasional redundant + lock-free relativistic model provides the necessary consistency, occasional redundant additions or deletions may happen. If this option is turned on then midPoint will read the data from resource right before the operation and filter our any redundant changes. This requires additional operation and it increases the risk of inconsistencies. However diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/performance/AbstractSchemaPerformanceTest.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/performance/AbstractSchemaPerformanceTest.java index d72640979f9..fcca3fbefdc 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/performance/AbstractSchemaPerformanceTest.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/performance/AbstractSchemaPerformanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2018 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -10,18 +10,16 @@ import java.io.File; import java.io.IOException; -import com.evolveum.midpoint.prism.impl.match.MatchingRuleRegistryFactory; -import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; import org.javasimon.Split; import org.javasimon.Stopwatch; import org.jetbrains.annotations.NotNull; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeSuite; import org.xml.sax.SAXException; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.impl.match.MatchingRuleRegistryFactory; +import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.schema.MidPointPrismContextFactory; import com.evolveum.midpoint.schema.constants.MidPointConstants; @@ -34,7 +32,7 @@ import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; -public abstract class AbstractSchemaPerformanceTest extends AbstractUnitTest implements PerformanceTestClassMixin { +public abstract class AbstractSchemaPerformanceTest extends AbstractUnitTest implements PerformanceTestClassMixin { protected static final String LABEL = "new-mapxnode"; @@ -59,12 +57,6 @@ public void setup() throws SchemaException, SAXException, IOException { assert !InternalsConfig.isConsistencyChecks(); } - @BeforeClass - @Override - public void initTestMonitor() { - PerformanceTestClassMixin.super.initTestMonitor(); - } - protected void measure(String label, String note, CheckedProducer producer) throws CommonException, IOException { measure(label, note, producer, DEFAULT_EXECUTION, DEFAULT_REPEATS); } @@ -102,10 +94,4 @@ protected double measureSingle(String label, CheckedProducer producer, long e public PrismObject getJack() throws SchemaException, IOException { return getPrismContext().parserFor(USER_JACK_FILE).parse(); } - - @AfterClass - @Override - public void dumpReport() { - PerformanceTestClassMixin.super.dumpReport(); - } } diff --git a/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/AbstractSpringTest.java b/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/AbstractSpringTest.java index 71a29dfc8a1..22ca341fb36 100644 --- a/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/AbstractSpringTest.java +++ b/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/AbstractSpringTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2020 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,6 +7,7 @@ package com.evolveum.midpoint.test.util; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import org.jetbrains.annotations.Nullable; @@ -17,10 +18,7 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; -import com.evolveum.midpoint.tools.testng.MidpointTestContext; -import com.evolveum.midpoint.tools.testng.MidpointTestMixin; -import com.evolveum.midpoint.tools.testng.SimpleMidpointTestContext; -import com.evolveum.midpoint.tools.testng.TestMonitor; +import com.evolveum.midpoint.tools.testng.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -37,6 +35,7 @@ public abstract class AbstractSpringTest extends AbstractTestNGSpringContextTest */ protected final Trace logger = TraceManager.getTrace(getClass()); + // region perf-test support private TestMonitor testMonitor; /** Called only by tests that need it, implements performance mixin interface. */ @@ -55,6 +54,40 @@ public TestMonitor testMonitor() { return testMonitor; } + // see the comment in PerformanceTestMethodMixin for explanation + @BeforeMethod + public void initTestMethodMonitor() { + if (this instanceof PerformanceTestMethodMixin) { + createTestMonitor(); + } + } + + // see the comment in PerformanceTestMethodMixin for explanation + @AfterMethod + public void dumpMethodReport(Method method) { + if (this instanceof PerformanceTestMethodMixin) { + ((PerformanceTestMethodMixin) this).dumpReport( + getClass().getSimpleName() + "#" + method.getName()); + } + } + + // see the comment in PerformanceTestClassMixin for explanation + @BeforeClass + public void initTestClassMonitor() { + if (this instanceof PerformanceTestClassMixin) { + createTestMonitor(); + } + } + + // see the comment in PerformanceTestClassMixin for explanation + @AfterClass + public void dumpClassReport() { + if (this instanceof PerformanceTestClassMixin) { + ((PerformanceTestClassMixin) this).dumpReport(getClass().getSimpleName()); + } + } + // endregion + @BeforeClass public void displayTestClassTitle() { displayTestTitle("Starting TEST CLASS: " + getClass().getName()); @@ -91,18 +124,18 @@ public MidpointTestContext getTestContext() { /** * This method null all fields which are not static, final or primitive type. - *

+ * * All this is just to make GC work during DirtiesContext after every test class, * because test class instances are not GCed immediately. * If they hold autowired fields like sessionFactory (for example * through SqlRepositoryService impl), their memory footprint is getting big. * This can manifest as failed test initialization because of OOM in modules like model-intest. * Strangely, this will not fail the Jenkins build (but makes it much slower). - *

+ * * Note that this does not work for components injected through constructor into * final fields - if we need this cleanup, make the field non-final. */ - @AfterClass(alwaysRun = true) + @AfterClass(alwaysRun = true, dependsOnMethods = "dumpClassReport") protected void clearClassFields() throws Exception { logger.trace("Clearing all fields for test class {}", getClass().getName()); clearClassFields(this, getClass()); @@ -114,9 +147,7 @@ private void clearClassFields(Object object, Class forClass) throws Exception } for (Field field : forClass.getDeclaredFields()) { - // we need to skip testMonitor to have it non-null in PerformanceTestMixin#dumpReport - if (field.getName().equals("testMonitor") - || Modifier.isFinal(field.getModifiers()) + if (Modifier.isFinal(field.getModifiers()) || Modifier.isStatic(field.getModifiers()) || field.getType().isPrimitive()) { continue; diff --git a/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/TestReportUtil.java b/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/TestReportUtil.java index dc2028284f0..5132804b409 100644 --- a/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/TestReportUtil.java +++ b/infra/test-util/src/main/java/com/evolveum/midpoint/test/util/TestReportUtil.java @@ -6,14 +6,15 @@ */ package com.evolveum.midpoint.test.util; -import java.util.Comparator; +import static com.evolveum.midpoint.schema.statistics.AbstractStatisticsPrinter.Format.RAW; +import static com.evolveum.midpoint.schema.statistics.AbstractStatisticsPrinter.SortBy.NAME; +import static com.evolveum.midpoint.schema.statistics.AbstractStatisticsPrinter.SortBy.TIME; -import com.evolveum.midpoint.schema.statistics.OperationsPerformanceInformationUtil; +import com.evolveum.midpoint.schema.statistics.*; import com.evolveum.midpoint.tools.testng.TestMonitor; import com.evolveum.midpoint.tools.testng.TestReportSection; import com.evolveum.midpoint.util.statistics.OperationsPerformanceMonitor; -import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationsPerformanceInformationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.SingleOperationPerformanceInformationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; public class TestReportUtil { @@ -25,37 +26,84 @@ public static void reportGlobalPerfData(TestMonitor testMonitor) { OperationsPerformanceInformationType performanceInformation = OperationsPerformanceInformationUtil.toOperationsPerformanceInformationType( OperationsPerformanceMonitor.INSTANCE.getGlobalPerformanceInformation()); - TestReportSection section = testMonitor.addReportSection("globalPerformanceInformation") - .withColumns("Operation", "Count", "Total time (ms)", "Min", "Max", "Avg"); - - performanceInformation.getOperation().stream() - .sorted(Comparator.comparing(SingleOperationPerformanceInformationType::getTotalTime).reversed()) - .forEach(op -> { - int count = zeroIfNull(op.getInvocationCount()); - float totalTime = zeroIfNull(op.getTotalTime()) / 1000.0f; - section.addRow( - op.getName(), - count, - totalTime, - zeroIfNull(op.getMinTime()) / 1000.0f, - zeroIfNull(op.getMaxTime()) / 1000.0f, - avg(totalTime, count)); - }); - // TODO: How to adapt this to the code above? See OperationsPerformanceInformationPrinter for the details. -// OperationsPerformanceInformationUtil.format(performanceInformation, -// new AbstractStatisticsPrinter.Options(CSV, TIME), null, null)); + + OperationsPerformanceInformationPrinter printer = new OperationsPerformanceInformationPrinter(performanceInformation, + new AbstractStatisticsPrinter.Options(RAW, TIME), null, null, false); + + addPrinterData(testMonitor, "globalPerformanceInformation", printer); } - // TODO: cleanup! taken from AbstractStatisticsPrinter - private static int zeroIfNull(Integer n) { - return n != null ? n : 0; + private static void addPrinterData(TestMonitor testMonitor, String sectionName, AbstractStatisticsPrinter printer) { + printer.prepare(); + Data data = printer.getData(); + Formatting formatting = printer.getFormatting(); + + TestReportSection section = testMonitor.addReportSection(sectionName) + .withColumns(formatting.getColumnLabels().toArray(new String[0])); + data.getRawDataStream().forEach(section::addRow); } - private static long zeroIfNull(Long n) { - return n != null ? n : 0; + /** + * Adds operation performance for a given task to the {@link TestMonitor}. + */ + public static void reportTaskOperationPerformance(TestMonitor testMonitor, String label, + TaskType task, Integer iterations, Integer seconds) { + OperationsPerformanceInformationType performanceInformationFromTask = + task.getOperationStats() != null ? task.getOperationStats().getOperationsPerformanceInformation() : null; + OperationsPerformanceInformationType performanceInformation = performanceInformationFromTask != null ? + performanceInformationFromTask : new OperationsPerformanceInformationType(); + + OperationsPerformanceInformationPrinter printer = new OperationsPerformanceInformationPrinter(performanceInformation, + new AbstractStatisticsPrinter.Options(RAW, TIME), iterations, seconds, false); + + addPrinterData(testMonitor, label + ":operationPerformance", printer); } - private static Number avg(Number total, int count) { - return total != null && count > 0 ? total.floatValue() / count : null; + /** + * Adds repository performance for a given task to the {@link TestMonitor}. + */ + public static void reportTaskRepositoryPerformance(TestMonitor testMonitor, String label, + TaskType task, Integer iterations, Integer seconds) { + RepositoryPerformanceInformationType performanceInformationFromTask = + task.getOperationStats() != null ? task.getOperationStats().getRepositoryPerformanceInformation() : null; + RepositoryPerformanceInformationType performanceInformation = performanceInformationFromTask != null ? + performanceInformationFromTask : new RepositoryPerformanceInformationType(); + + RepositoryPerformanceInformationPrinter printer = new RepositoryPerformanceInformationPrinter(performanceInformation, + new AbstractStatisticsPrinter.Options(RAW, NAME), iterations, seconds); + + addPrinterData(testMonitor, label + ":repositoryPerformance", printer); + } + + /** + * Adds caches performance for a given task to the {@link TestMonitor}. + */ + public static void reportTaskCachesPerformance(TestMonitor testMonitor, String label, TaskType task) { + CachesPerformanceInformationType performanceInformationFromTask = + task.getOperationStats() != null ? task.getOperationStats().getCachesPerformanceInformation() : null; + CachesPerformanceInformationType performanceInformation = performanceInformationFromTask != null ? + performanceInformationFromTask : new CachesPerformanceInformationType(); + + CachePerformanceInformationPrinter printer = new CachePerformanceInformationPrinter(performanceInformation, + new AbstractStatisticsPrinter.Options(RAW, NAME)); + + addPrinterData(testMonitor, label + ":cachePerformance", printer); + } + + /** + * Adds provisioning operations statistics for a given task to the {@link TestMonitor}. + */ + public static void reportTaskProvisioningStatistics(TestMonitor testMonitor, String label, TaskType task) { + EnvironmentalPerformanceInformationType envPerformanceInformationFromTask = + task.getOperationStats() != null ? task.getOperationStats().getEnvironmentalPerformanceInformation() : null; + ProvisioningStatisticsType provisioningStatisticsFromTask = envPerformanceInformationFromTask != null ? + envPerformanceInformationFromTask.getProvisioningStatistics() : null; + ProvisioningStatisticsType provisioningStatistics = provisioningStatisticsFromTask != null ? + provisioningStatisticsFromTask : new ProvisioningStatisticsType(); + + ProvisioningStatisticsPrinter printer = new ProvisioningStatisticsPrinter(provisioningStatistics, + new AbstractStatisticsPrinter.Options(RAW, NAME)); + + addPrinterData(testMonitor, label + ":provisioningStatistics", printer); } } diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index e23743dec49..adbbdaa0f7d 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -269,7 +269,7 @@ protected DummyResourceContoller initDummyResource(String name, File resourceFil return dummyResourceCollection.initDummyResource(name, resourceFile, resourceOid, controllerInitLambda, task, result); } - protected DummyResourceContoller initDummyResource(DummyTestResource resource, Task task, OperationResult result) throws Exception { + public DummyResourceContoller initDummyResource(DummyTestResource resource, Task task, OperationResult result) throws Exception { resource.controller = dummyResourceCollection.initDummyResource(resource.name, resource.file, resource.oid, resource.controllerInitLambda, task, result); return resource.controller; diff --git a/pom.xml b/pom.xml index 298af2c8cf3..1931e566461 100644 --- a/pom.xml +++ b/pom.xml @@ -54,9 +54,17 @@ mvn clean install -P -dist -DskipTests -DskipStoryTests=false See "extratest" profile in testing/pom.xml for all individual properties enabling extra tests. + Running a single IT test class from story tests with remote debugger: + mvn integration-test -DskipStoryTests=false -DredirectTestOutputToFile=false \ + -Dmaven.failsafe.debug="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" \ + -pl testing/story -Dit.test=TestSystemPerformance + Note the "maven.failsafe.debug", it is possible to use "maven.surefire.debug" (with -Dtest) too. + Change to "suspend=y" for the test to wait for the debugger (e.g. debugging the startup). + See: https://maven.apache.org/surefire/maven-surefire-plugin/examples/debugging.html + and https://maven.apache.org/surefire/maven-failsafe-plugin/examples/debugging.html + Running unit+IT tests of a selected module (-pl) with output to stdout: mvn clean install -DredirectTestOutputToFile=false -pl model/model-intest - --> diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index 1e11f1f6dff..07f468f6c68 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -187,7 +187,7 @@ $$; -- URI can be anything, for QNames the format is based on QNameUtil ("prefix-url#localPart"). CREATE TABLE m_uri ( id SERIAL NOT NULL PRIMARY KEY, - uri TEXT/*VARCHAR(255)*/ NOT NULL UNIQUE + uri TEXT NOT NULL UNIQUE ); -- There can be more constants pre-filled, but that adds overhead, let the first-start do it. @@ -215,13 +215,13 @@ CREATE TABLE m_object ( oid UUID NOT NULL, -- objectType will be overridden with GENERATED value in concrete table objectType ObjectType NOT NULL, - name_orig TEXT/*VARCHAR(255)*/ NOT NULL, - name_norm TEXT/*VARCHAR(255)*/ NOT NULL, + name_orig TEXT NOT NULL, + name_norm TEXT NOT NULL, fullObject BYTEA, tenantRef_targetOid UUID, tenantRef_targetType ObjectType, tenantRef_relation_id INTEGER REFERENCES m_uri(id), - lifecycleState TEXT/*VARCHAR(255)*/, -- TODO what is this? how many distinct values? + lifecycleState TEXT, -- TODO what is this? how many distinct values? cid_seq BIGINT NOT NULL DEFAULT 1, -- sequence for container id, next free cid version INTEGER NOT NULL DEFAULT 1, -- complex DB columns, add indexes as needed per concrete table, e.g. see m_user @@ -366,15 +366,15 @@ CREATE INDEX m_ref_role_member_targetOid_relation_id_idx CREATE TABLE m_focus ( -- will be overridden with GENERATED value in concrete table objectType ObjectType NOT NULL, - costCenter TEXT/*VARCHAR(255)*/, - emailAddress TEXT/*VARCHAR(255)*/, + costCenter TEXT, + emailAddress TEXT, photo BYTEA, -- will be TOAST-ed if necessary - locale TEXT/*VARCHAR(255)*/, - locality_orig TEXT/*VARCHAR(255)*/, - locality_norm TEXT/*VARCHAR(255)*/, - preferredLanguage TEXT/*VARCHAR(255)*/, - telephoneNumber TEXT/*VARCHAR(255)*/, - timezone TEXT/*VARCHAR(255)*/, + locale TEXT, + locality_orig TEXT, + locality_norm TEXT, + preferredLanguage TEXT, + telephoneNumber TEXT, + timezone TEXT, -- credential/password/metadata passwordCreateTimestamp TIMESTAMPTZ, passwordModifyTimestamp TIMESTAMPTZ, @@ -383,7 +383,7 @@ CREATE TABLE m_focus ( effectiveStatus ActivationStatusType, enableTimestamp TIMESTAMPTZ, disableTimestamp TIMESTAMPTZ, - disableReason TEXT/*VARCHAR(255)*/, + disableReason TEXT, validityStatus TimeIntervalStatusType, validFrom TIMESTAMPTZ, validTo TIMESTAMPTZ, @@ -445,23 +445,23 @@ ALTER TABLE m_generic_object ADD CONSTRAINT m_generic_object_name_norm_key UNIQU CREATE TABLE m_user ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('USER') STORED, - additionalName_orig TEXT/*VARCHAR(255)*/, - additionalName_norm TEXT/*VARCHAR(255)*/, - employeeNumber TEXT/*VARCHAR(255)*/, - familyName_orig TEXT/*VARCHAR(255)*/, - familyName_norm TEXT/*VARCHAR(255)*/, - fullName_orig TEXT/*VARCHAR(255)*/, - fullName_norm TEXT/*VARCHAR(255)*/, - givenName_orig TEXT/*VARCHAR(255)*/, - givenName_norm TEXT/*VARCHAR(255)*/, - honorificPrefix_orig TEXT/*VARCHAR(255)*/, - honorificPrefix_norm TEXT/*VARCHAR(255)*/, - honorificSuffix_orig TEXT/*VARCHAR(255)*/, - honorificSuffix_norm TEXT/*VARCHAR(255)*/, - nickName_orig TEXT/*VARCHAR(255)*/, - nickName_norm TEXT/*VARCHAR(255)*/, - title_orig TEXT/*VARCHAR(255)*/, - title_norm TEXT/*VARCHAR(255)*/ + additionalName_orig TEXT, + additionalName_norm TEXT, + employeeNumber TEXT, + familyName_orig TEXT, + familyName_norm TEXT, + fullName_orig TEXT, + fullName_norm TEXT, + givenName_orig TEXT, + givenName_norm TEXT, + honorificPrefix_orig TEXT, + honorificPrefix_norm TEXT, + honorificSuffix_orig TEXT, + honorificSuffix_norm TEXT, + nickName_orig TEXT, + nickName_norm TEXT, + title_orig TEXT, + title_norm TEXT ) INHERITS (m_focus); @@ -484,13 +484,13 @@ CREATE INDEX m_user_employeeNumber_idx ON m_user (employeeNumber); /* TODO JSON of polystrings? CREATE TABLE m_user_organization ( user_oid UUID NOT NULL, - norm TEXT/*VARCHAR(255)*/, - orig TEXT/*VARCHAR(255)*/ + norm TEXT, + orig TEXT ); CREATE TABLE m_user_organizational_unit ( user_oid UUID NOT NULL, - norm TEXT/*VARCHAR(255)*/, - orig TEXT/*VARCHAR(255)*/ + norm TEXT, + orig TEXT ); */ -- endregion @@ -501,11 +501,11 @@ CREATE TABLE m_abstract_role ( -- will be overridden with GENERATED value in concrete table objectType ObjectType NOT NULL, autoAssignEnabled BOOLEAN, - displayName_orig TEXT/*VARCHAR(255)*/, - displayName_norm TEXT/*VARCHAR(255)*/, - identifier TEXT/*VARCHAR(255)*/, + displayName_orig TEXT, + displayName_norm TEXT, + identifier TEXT, requestable BOOLEAN, - riskLevel TEXT/*VARCHAR(255)*/, + riskLevel TEXT, CHECK (FALSE) NO INHERIT ) @@ -515,7 +515,7 @@ CREATE TABLE m_abstract_role ( CREATE TABLE m_role ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('ROLE') STORED, - roleType TEXT/*VARCHAR(255)*/ + roleType TEXT ) INHERITS (m_abstract_role); @@ -627,7 +627,7 @@ CREATE TABLE m_access_cert_case ( containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_CASE') STORED, administrativeStatus INTEGER, archiveTimestamp TIMESTAMPTZ, - disableReason TEXT/*VARCHAR(255)*/, + disableReason TEXT, disableTimestamp TIMESTAMPTZ, effectiveStatus INTEGER, enableTimestamp TIMESTAMPTZ, @@ -635,7 +635,7 @@ CREATE TABLE m_access_cert_case ( validTo TIMESTAMPTZ, validityChangeTimestamp TIMESTAMPTZ, validityStatus INTEGER, - currentStageOutcome TEXT/*VARCHAR(255)*/, + currentStageOutcome TEXT, fullObject BYTEA, iteration INTEGER NOT NULL, objectRef_targetOid UUID, @@ -644,7 +644,7 @@ CREATE TABLE m_access_cert_case ( orgRef_targetOid UUID, orgRef_targetType ObjectType, orgRef_relation_id INTEGER REFERENCES m_uri(id), - outcome TEXT/*VARCHAR(255)*/, + outcome TEXT, remediedTimestamp TIMESTAMPTZ, reviewDeadline TIMESTAMPTZ, reviewRequestedTimestamp TIMESTAMPTZ, @@ -666,7 +666,7 @@ CREATE TABLE m_access_cert_wi ( containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_WORK_ITEM') STORED, closeTimestamp TIMESTAMPTZ, iteration INTEGER NOT NULL, - outcome TEXT/*VARCHAR(255)*/, + outcome TEXT, outputChangeTimestamp TIMESTAMPTZ, performerRef_targetOid UUID, performerRef_targetType ObjectType, @@ -758,14 +758,14 @@ CREATE TABLE m_shadow ( resourceRef_targetOid UUID, resourceRef_targetType ObjectType, resourceRef_relation_id INTEGER REFERENCES m_uri(id), - intent TEXT/*VARCHAR(255)*/, + intent TEXT, kind ShadowKindType, attemptNumber INTEGER, -- TODO how is this mapped? dead BOOLEAN, exist BOOLEAN, fullSynchronizationTimestamp TIMESTAMPTZ, pendingOperationCount INTEGER, - primaryIdentifierValue TEXT/*VARCHAR(255)*/, + primaryIdentifierValue TEXT, -- status INTEGER, TODO how is this mapped? See RUtil.copyResultFromJAXB called from RTask and OperationResultMapper synchronizationSituation SynchronizationSituationType, synchronizationTimestamp TIMESTAMPTZ @@ -804,7 +804,7 @@ ALTER TABLE m_shadow ADD CONSTRAINT iPrimaryIdentifierValueWithOC CREATE TABLE m_node ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('NODE') STORED, - nodeIdentifier TEXT/*VARCHAR(255)*/ + nodeIdentifier TEXT ) INHERITS (m_object); @@ -963,10 +963,10 @@ ALTER TABLE m_lookup_table ADD CONSTRAINT m_lookup_table_name_norm_key UNIQUE (n CREATE TABLE m_lookup_table_row ( owner_oid UUID NOT NULL REFERENCES m_lookup_table(oid) ON DELETE CASCADE, containerType ContainerType GENERATED ALWAYS AS ('LOOKUP_TABLE_ROW') STORED, - key TEXT/*VARCHAR(255)*/, - value TEXT/*VARCHAR(255)*/, - label_orig TEXT/*VARCHAR(255)*/, - label_norm TEXT/*VARCHAR(255)*/, + key TEXT, + value TEXT, + label_orig TEXT, + label_norm TEXT, lastChangeTimestamp TIMESTAMPTZ, PRIMARY KEY (owner_oid, cid) @@ -980,9 +980,9 @@ ALTER TABLE m_lookup_table_row CREATE TABLE m_connector ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('CONNECTOR') STORED, - connectorBundle TEXT/*VARCHAR(255)*/, -- typically a package name - connectorType TEXT/*VARCHAR(255)*/, -- typically a class name - connectorVersion TEXT/*VARCHAR(255)*/, + connectorBundle TEXT, -- typically a package name + connectorType TEXT, -- typically a class name + connectorVersion TEXT, framework_id INTEGER REFERENCES m_uri(id), connectorHostRef_targetOid UUID, connectorHostRef_targetType ObjectType, @@ -1004,7 +1004,7 @@ ALTER TABLE m_connector ADD CONSTRAINT m_connector_name_norm_key UNIQUE (name_no -- TODO array/json in m_connector table -- CREATE TABLE m_connector_target_system ( -- connector_oid UUID NOT NULL, --- targetSystemType TEXT/*VARCHAR(255)*/ +-- targetSystemType TEXT -- ); -- ALTER TABLE m_connector_target_system -- ADD CONSTRAINT fk_connector_target_system FOREIGN KEY (connector_oid) REFERENCES m_connector; @@ -1013,8 +1013,8 @@ ALTER TABLE m_connector ADD CONSTRAINT m_connector_name_norm_key UNIQUE (name_no CREATE TABLE m_connector_host ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('CONNECTOR_HOST') STORED, - hostname TEXT/*VARCHAR(255)*/, - port TEXT/*VARCHAR(32)*/ + hostname TEXT, + port TEXT ) INHERITS (m_object); @@ -1032,23 +1032,23 @@ ALTER TABLE m_connector_host ADD CONSTRAINT m_connector_host_name_norm_key UNIQU CREATE TABLE m_task ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('TASK') STORED, - taskIdentifier TEXT/*VARCHAR(255)*/, + taskIdentifier TEXT, binding TaskBindingType, - category TEXT/*VARCHAR(255)*/, + category TEXT, completionTimestamp TIMESTAMPTZ, executionStatus TaskExecutionStateType, fullResult BYTEA, handlerUri_id INTEGER REFERENCES m_uri(id), lastRunStartTimestamp TIMESTAMPTZ, lastRunFinishTimestamp TIMESTAMPTZ, - node TEXT/*VARCHAR(255)*/, -- node_id only for information purposes + node TEXT, -- node_id only for information purposes objectRef_targetOid UUID, objectRef_targetType ObjectType, objectRef_relation_id INTEGER REFERENCES m_uri(id), ownerRef_targetOid UUID, ownerRef_targetType ObjectType, ownerRef_relation_id INTEGER REFERENCES m_uri(id), - parent TEXT/*VARCHAR(255)*/, -- value of taskIdentifier + parent TEXT, -- value of taskIdentifier recurrence TaskRecurrenceType, resultStatus OperationResultStatusType, threadStopAction ThreadStopActionType, @@ -1075,7 +1075,7 @@ CREATE INDEX m_task_dependentTaskIdentifiers_idx ON m_task USING GIN(dependentTa CREATE TABLE m_case ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('CASE') STORED, - state TEXT/*VARCHAR(255)*/, + state TEXT, closeTimestamp TIMESTAMPTZ, objectRef_targetOid UUID, objectRef_targetType ObjectType, @@ -1198,21 +1198,19 @@ CREATE INDEX m_form_name_orig_idx ON m_form (name_orig); ALTER TABLE m_form ADD CONSTRAINT m_form_name_norm_key UNIQUE (name_norm); -- endregion --- region Assignment/Inducement tables +-- region Assignment/Inducement table -- Represents AssignmentType, see https://wiki.evolveum.com/display/midPoint/Assignment -- and also https://wiki.evolveum.com/display/midPoint/Assignment+vs+Inducement --- TODO: if we never need mix of inducements and assignments then let's have two separate tables --- consult with Rado/Katka/Palo --- TODO: partitioning, not by object type, it's not even... hash-something? +-- TODO: partitioning, probably not by object type, it's not even... hash-something? -- select assignmentowner, count(*) From m_assignment group by assignmentowner; --1 45 (inducements) --0 48756229 --- abstract common structure for m_assignment and m_inducement -CREATE TABLE m_assignment_type ( - -- owner_oid + containerType from m_container, final specification in sub-tables - -- new column may avoid join to object for some queries +CREATE TABLE m_assignment ( + owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + -- this is different from other containers, this is not generated, app must provide it + containerType ContainerType NOT NULL CHECK (containerType IN ('ASSIGNMENT', 'INDUCEMENT')), owner_type ObjectType NOT NULL, - lifecycleState TEXT/*VARCHAR(255)*/, + lifecycleState TEXT, orderValue INTEGER, orgRef_targetOid UUID, orgRef_targetType ObjectType, @@ -1225,7 +1223,7 @@ CREATE TABLE m_assignment_type ( tenantRef_relation_id INTEGER REFERENCES m_uri(id), -- TODO what is this? see RAssignment.getExtension (both extId/Oid) extId INTEGER, - extOid TEXT/*VARCHAR(36)*/, -- is this UUID too? + extOid TEXT, -- is this UUID too? policySituations INTEGER[], -- soft-references m_uri, add index per table ext JSONB, -- construction @@ -1237,7 +1235,7 @@ CREATE TABLE m_assignment_type ( effectiveStatus ActivationStatusType, enableTimestamp TIMESTAMPTZ, disableTimestamp TIMESTAMPTZ, - disableReason TEXT/*VARCHAR(255)*/, + disableReason TEXT, validityStatus TimeIntervalStatusType, validFrom TIMESTAMPTZ, validTo TIMESTAMPTZ, @@ -1255,21 +1253,9 @@ CREATE TABLE m_assignment_type ( modifyChannel_id INTEGER, modifyTimestamp TIMESTAMPTZ, - -- create PRIMARY KEY (owner_oid, cid) on sub-tables - -- no need to index owner_oid, it's part of the PK index - - CHECK (FALSE) NO INHERIT -) - INHERITS(m_container); - -CREATE TABLE m_assignment ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - containerType ContainerType GENERATED ALWAYS AS ('ASSIGNMENT') STORED, - PRIMARY KEY (owner_oid, cid) - -- no need to index owner_oid, it's part of the PK index ) - INHERITS(m_assignment_type); + INHERITS(m_container); CREATE INDEX m_assignment_policySituation_idx ON m_assignment USING GIN(policysituations gin__int_ops); CREATE INDEX m_assignment_ext_idx ON m_assignment USING gin (ext); @@ -1313,25 +1299,6 @@ ALTER TABLE m_assignment_ref_modify_approver ADD CONSTRAINT m_assignment_ref_mod FOREIGN KEY (owner_oid, assignment_cid) REFERENCES m_assignment (owner_oid, cid); -- TODO index targetOid, relation_id? - -CREATE TABLE m_inducement ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - containerType ContainerType GENERATED ALWAYS AS ('INDUCEMENT') STORED, - - PRIMARY KEY (owner_oid, cid) - -- no need to index owner_oid, it's part of the PK index -) - INHERITS(m_assignment_type); - -CREATE INDEX m_inducement_ext_idx ON m_inducement USING gin (ext); -CREATE INDEX m_inducement_validFrom_idx ON m_inducement (validFrom); -CREATE INDEX m_inducement_validTo_idx ON m_inducement (validTo); -CREATE INDEX m_inducement_targetRef_targetOid_idx ON m_inducement (targetRef_targetOid); -CREATE INDEX m_inducement_tenantRef_targetOid_idx ON m_inducement (tenantRef_targetOid); -CREATE INDEX m_inducement_orgRef_targetOid_idx ON m_inducement (orgRef_targetOid); -CREATE INDEX m_inducement_resourceRef_targetOid_idx ON m_inducement (resourceRef_targetOid); - --- TODO other tables like for assignments needed? policy situations, refs? -- endregion -- region Other object containers @@ -1380,10 +1347,10 @@ CREATE INDEX m_operation_execution_timestampValue_idx ON m_operation_execution ( CREATE TABLE m_ext_item ( id SERIAL NOT NULL, kind INTEGER, -- see RItemKind, is this necessary? does it contain cardinality? - name TEXT/*VARCHAR(157)*/, -- no path for nested props needed? - type TEXT/*VARCHAR(32)*/, -- data type TODO enum? - storageType TEXT/*VARCHAR(32)*/ NOT NULL default 'EXT_JSON', -- type of storage (JSON, column, table separate/common, etc.) - storageInfo TEXT/*VARCHAR(32)*/, -- optional storage detail, name of column or table if necessary + name TEXT, -- no path for nested props needed? + type TEXT, -- data type TODO enum? + storageType TEXT NOT NULL default 'EXT_JSON', -- type of storage (JSON, column, table separate/common, etc.) + storageInfo TEXT, -- optional storage detail, name of column or table if necessary PRIMARY KEY (id) ); @@ -1412,8 +1379,8 @@ CREATE TABLE m_object_ext_long ( CREATE TABLE m_object_ext_poly ( owner_oid UUID NOT NULL REFERENCES m_object_oid(oid), ext_item_id VARCHAR(32) NOT NULL, - orig TEXT/*VARCHAR(255)*/ NOT NULL, - norm TEXT/*VARCHAR(255)*/, + orig TEXT NOT NULL, + norm TEXT, PRIMARY KEY (owner_oid, ext_item_id, orig) ); CREATE TABLE m_object_ext_reference ( @@ -1427,7 +1394,7 @@ CREATE TABLE m_object_ext_reference ( CREATE TABLE m_object_ext_string ( owner_oid UUID NOT NULL REFERENCES m_object_oid(oid), ext_item_id VARCHAR(32) NOT NULL, - value TEXT/*VARCHAR(255)*/ NOT NULL, + value TEXT NOT NULL, PRIMARY KEY (owner_oid, ext_item_id, value) ); @@ -1463,8 +1430,8 @@ CREATE TABLE m_assignment_ext_poly ( item_id INTEGER NOT NULL, anyContainer_owner_id INTEGER NOT NULL, anyContainer_owner_owner_oid UUID NOT NULL, - orig TEXT/*VARCHAR(255)*/ NOT NULL, - norm TEXT/*VARCHAR(255)*/, + orig TEXT NOT NULL, + norm TEXT, PRIMARY KEY (anyContainer_owner_owner_oid, anyContainer_owner_id, item_id, orig) ); CREATE TABLE m_assignment_ext_reference ( @@ -1480,7 +1447,7 @@ CREATE TABLE m_assignment_ext_string ( item_id INTEGER NOT NULL, anyContainer_owner_id INTEGER NOT NULL, anyContainer_owner_owner_oid UUID NOT NULL, - stringValue TEXT/*VARCHAR(255)*/ NOT NULL, + stringValue TEXT NOT NULL, PRIMARY KEY (anyContainer_owner_owner_oid, anyContainer_owner_id, item_id, stringValue) ); CREATE TABLE m_assignment_extension ( @@ -1498,42 +1465,42 @@ CREATE TABLE m_audit_delta ( deltaOid UUID, deltaType INTEGER, fullResult BYTEA, - objectName_norm TEXT/*VARCHAR(255)*/, - objectName_orig TEXT/*VARCHAR(255)*/, - resourceName_norm TEXT/*VARCHAR(255)*/, - resourceName_orig TEXT/*VARCHAR(255)*/, + objectName_norm TEXT, + objectName_orig TEXT, + resourceName_norm TEXT, + resourceName_orig TEXT, resourceOid UUID, status INTEGER, PRIMARY KEY (record_id, checksum) ); CREATE TABLE m_audit_event ( id BIGSERIAL NOT NULL, - attorneyName TEXT/*VARCHAR(255)*/, + attorneyName TEXT, attorneyOid UUID, - channel TEXT/*VARCHAR(255)*/, - eventIdentifier TEXT/*VARCHAR(255)*/, + channel TEXT, + eventIdentifier TEXT, eventStage INTEGER, eventType INTEGER, - hostIdentifier TEXT/*VARCHAR(255)*/, - initiatorName TEXT/*VARCHAR(255)*/, + hostIdentifier TEXT, + initiatorName TEXT, initiatorOid UUID, initiatorType INTEGER, message VARCHAR(1024), - nodeIdentifier TEXT/*VARCHAR(255)*/, + nodeIdentifier TEXT, outcome INTEGER, - parameter TEXT/*VARCHAR(255)*/, - remoteHostAddress TEXT/*VARCHAR(255)*/, - requestIdentifier TEXT/*VARCHAR(255)*/, - result TEXT/*VARCHAR(255)*/, - sessionIdentifier TEXT/*VARCHAR(255)*/, - targetName TEXT/*VARCHAR(255)*/, + parameter TEXT, + remoteHostAddress TEXT, + requestIdentifier TEXT, + result TEXT, + sessionIdentifier TEXT, + targetName TEXT, targetOid UUID, - targetOwnerName TEXT/*VARCHAR(255)*/, + targetOwnerName TEXT, targetOwnerOid UUID, targetOwnerType INTEGER, targetType INTEGER, - taskIdentifier TEXT/*VARCHAR(255)*/, - taskOID TEXT/*VARCHAR(255)*/, + taskIdentifier TEXT, + taskOID TEXT, timestampValue TIMESTAMPTZ, PRIMARY KEY (id) ); @@ -1544,23 +1511,23 @@ CREATE TABLE m_audit_item ( ); CREATE TABLE m_audit_prop_value ( id BIGSERIAL NOT NULL, - name TEXT/*VARCHAR(255)*/, + name TEXT, record_id BIGINT, value VARCHAR(1024), PRIMARY KEY (id) ); CREATE TABLE m_audit_ref_value ( id BIGSERIAL NOT NULL, - name TEXT/*VARCHAR(255)*/, + name TEXT, oid UUID, record_id BIGINT, - targetName_norm TEXT/*VARCHAR(255)*/, - targetName_orig TEXT/*VARCHAR(255)*/, - type TEXT/*VARCHAR(255)*/, + targetName_norm TEXT, + targetName_orig TEXT, + type TEXT, PRIMARY KEY (id) ); CREATE TABLE m_audit_resource ( - resourceOid TEXT/*VARCHAR(255)*/ NOT NULL, + resourceOid TEXT NOT NULL, record_id BIGINT NOT NULL, PRIMARY KEY (record_id, resourceOid) ); @@ -1573,7 +1540,7 @@ CREATE TABLE m_case_wi ( originalAssigneeRef_relation VARCHAR(157), originalAssigneeRef_targetOid UUID, originalAssigneeRef_targetType INTEGER, - outcome TEXT/*VARCHAR(255)*/, + outcome TEXT, performerRef_relation VARCHAR(157), performerRef_targetOid UUID, performerRef_targetType INTEGER, @@ -1621,8 +1588,8 @@ CREATE TABLE m_object_ext_poly ( item_id INTEGER NOT NULL, owner_oid UUID NOT NULL, ownerType INTEGER NOT NULL, - orig TEXT/*VARCHAR(255)*/ NOT NULL, - norm TEXT/*VARCHAR(255)*/, + orig TEXT NOT NULL, + norm TEXT, PRIMARY KEY (owner_oid, ownerType, item_id, orig) ); CREATE TABLE m_object_ext_reference ( @@ -1638,7 +1605,7 @@ CREATE TABLE m_object_ext_string ( item_id INTEGER NOT NULL, owner_oid UUID NOT NULL, ownerType INTEGER NOT NULL, - stringValue TEXT/*VARCHAR(255)*/ NOT NULL, + stringValue TEXT NOT NULL, PRIMARY KEY (owner_oid, ownerType, item_id, stringValue) ); CREATE TABLE m_org_closure ( @@ -1650,8 +1617,8 @@ CREATE TABLE m_org_closure ( CREATE TABLE m_org ( displayOrder INTEGER, - name_norm TEXT/*VARCHAR(255)*/, - name_orig TEXT/*VARCHAR(255)*/, + name_norm TEXT, + name_orig TEXT, tenant BOOLEAN, oid UUID NOT NULL, PRIMARY KEY (oid) @@ -2047,8 +2014,8 @@ create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP); -- region Schema versioning and upgrading CREATE TABLE m_global_metadata ( - name TEXT/*VARCHAR(255)*/ PRIMARY KEY, - value TEXT/*VARCHAR(255)*/ + name TEXT PRIMARY KEY, + value TEXT ); /* diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java index cffe745ec8d..1c603666c79 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java @@ -15,18 +15,16 @@ import com.evolveum.midpoint.repo.sqale.filtering.InOidFilterProcessor; import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping; import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; -import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; import com.evolveum.midpoint.repo.sqlbase.filtering.FilterProcessor; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; -import com.evolveum.midpoint.repo.sqlbase.mapping.SqlTransformer; import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; public class SqaleQueryContext, R> extends SqlQueryContext { public static , R> SqaleQueryContext from( - Class schemaType, SqaleTransformerSupport transformerSupport, - SqlRepoContext sqlRepoContext) { + Class schemaType, + SqaleRepoContext sqlRepoContext) { SqaleTableMapping rootMapping = sqlRepoContext.getMappingBySchemaType(schemaType); Q rootPath = rootMapping.defaultAlias(); @@ -36,21 +34,20 @@ public static , R> SqaleQueryContext< query.getMetadata().setValidate(true); return new SqaleQueryContext<>( - rootPath, rootMapping, transformerSupport, sqlRepoContext, query); + rootPath, rootMapping, sqlRepoContext, query); } private SqaleQueryContext( Q entityPath, SqaleTableMapping mapping, - SqaleTransformerSupport transformerSupport, - SqlRepoContext sqlRepoContext, + SqaleRepoContext sqlRepoContext, SQLQuery query) { - super(entityPath, mapping, sqlRepoContext, transformerSupport, query); + super(entityPath, mapping, sqlRepoContext, query); } @Override - protected SqlTransformer createTransformer() { - return entityPathMapping.createTransformer(transformerSupport); + public SqaleRepoContext repositoryContext() { + return (SqaleRepoContext) super.repositoryContext(); } @Override @@ -59,23 +56,19 @@ public FilterProcessor createInOidFilter(SqlQueryContext c } public @NotNull Integer searchCachedRelationId(QName qName) { - return transformerSupport().searchCachedRelationId(qName); + return repositoryContext().searchCachedRelationId(qName); } /** - * Returns {@link SqaleQueryContext} - lot of ugly casting here, but it is not possible to - * use covariant return type with covariant parametric types (narrower generics). + * Returns derived {@link SqaleQueryContext} for join or subquery. */ - @SuppressWarnings({ "rawtypes", "unchecked" }) @Override protected , TR> SqlQueryContext deriveNew(TQ newPath, QueryTableMapping newMapping) { - return (SqlQueryContext) new SqaleQueryContext( - newPath, (SqaleTableMapping) newMapping, - transformerSupport(), sqlRepoContext, sqlQuery); - } - - private SqaleTransformerSupport transformerSupport() { - return (SqaleTransformerSupport) transformerSupport; + return new SqaleQueryContext<>( + newPath, + (SqaleTableMapping) newMapping, + repositoryContext(), + sqlQuery); } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepoContext.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepoContext.java index b46b6eea22d..bfbc3dccb50 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepoContext.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepoContext.java @@ -14,12 +14,15 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; +import com.evolveum.midpoint.repo.sqale.qmodel.common.QUri; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; import com.evolveum.midpoint.repo.sqale.qmodel.ref.MReferenceType; import com.evolveum.midpoint.repo.sqlbase.JdbcRepositoryConfiguration; import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryModelMappingRegistry; import com.evolveum.midpoint.repo.sqlbase.querydsl.QuerydslJsonbType; +import com.evolveum.midpoint.schema.SchemaService; +import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; /** @@ -32,8 +35,9 @@ public class SqaleRepoContext extends SqlRepoContext { public SqaleRepoContext( JdbcRepositoryConfiguration jdbcRepositoryConfiguration, DataSource dataSource, + SchemaService schemaService, QueryModelMappingRegistry mappingRegistry) { - super(jdbcRepositoryConfiguration, dataSource, mappingRegistry); + super(jdbcRepositoryConfiguration, dataSource, schemaService, mappingRegistry); // each enum type must be registered if we want to map it as objects (to PG enum types) querydslConfig.register(new EnumAsObjectType<>(ActivationStatusType.class)); @@ -67,29 +71,32 @@ public void clearCaches() { uriCache.initialize(this::newJdbcSession); } - /** @see UriCache#searchId(QName) */ - @NotNull - public Integer searchCachedUriId(QName qName) { - return uriCache.searchId(qName); - } - /** @see UriCache#searchId(String) */ public Integer searchCachedUriId(String uri) { return uriCache.searchId(uri); } - /** @see UriCache#resolveUriToId(String) */ - public @NotNull Integer resolveUriToId(String uri) { - return uriCache.resolveUriToId(uri); - } - - /** @see UriCache#resolveUriToId(QName) */ - public Integer resolveUriToId(QName uri) { - return uriCache.resolveUriToId(uri); + /** + * Returns ID for relation QName or {@link UriCache#UNKNOWN_ID} without going to the database. + * Relation is normalized before consulting {@link UriCache}. + * Never returns null; returns default ID for configured default relation if provided with null. + */ + public @NotNull Integer searchCachedRelationId(QName qName) { + return searchCachedUriId(QNameUtil.qNameToUri(normalizeRelation(qName))); } /** Returns ID for URI creating new cache row in DB as needed. */ public Integer processCacheableUri(String uri) { return uriCache.processCacheableUri(uri); } + + /** + * Returns ID for relation QName creating new {@link QUri} row in DB as needed. + * Relation is normalized before consulting the cache. + * Never returns null, returns default ID for configured default relation. + */ + public Integer processCacheableRelation(QName qName) { + return processCacheableUri( + QNameUtil.qNameToUri(normalizeRelation(qName))); + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java index 800f72f08e5..408168f3cf4 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java @@ -110,49 +110,66 @@ public DataSource dataSource(DataSourceFactory dataSourceFactory) @Bean public SqaleRepoContext sqlRepoContext( SqaleRepositoryConfiguration repositoryConfiguration, + SchemaService schemaService, DataSource dataSource) { - QueryModelMappingRegistry mappingRegistry = new QueryModelMappingRegistry() - // ordered alphabetically here, mappings without schema type at the end - .register(AbstractRoleType.COMPLEX_TYPE, QAbstractRoleMapping.INSTANCE) + QueryModelMappingRegistry mappingRegistry = new QueryModelMappingRegistry(); + SqaleRepoContext repositoryContext = new SqaleRepoContext( + repositoryConfiguration, dataSource, schemaService, mappingRegistry); + + // Registered mapping needs repository context which needs registry. Now we can fill it. + // Mappings are ordered alphabetically here, mappings without schema type are at the end. + mappingRegistry + .register(AbstractRoleType.COMPLEX_TYPE, + QAbstractRoleMapping.init(repositoryContext)) .register(AccessCertificationDefinitionType.COMPLEX_TYPE, - QAccessCertificationDefinitionMapping.INSTANCE) - .register(ArchetypeType.COMPLEX_TYPE, QArchetypeMapping.INSTANCE) - .register(AssignmentHolderType.COMPLEX_TYPE, QAssignmentHolderMapping.INSTANCE) - .register(AssignmentType.COMPLEX_TYPE, QAssignmentMapping.INSTANCE) - .register(CaseType.COMPLEX_TYPE, QCaseMapping.INSTANCE) - .register(DashboardType.COMPLEX_TYPE, QDashboardMapping.INSTANCE) - .register(FocusType.COMPLEX_TYPE, QFocusMapping.INSTANCE) - .register(FormType.COMPLEX_TYPE, QFormMapping.INSTANCE) - .register(FunctionLibraryType.COMPLEX_TYPE, QFunctionLibraryMapping.INSTANCE) - .register(ConnectorType.COMPLEX_TYPE, QConnectorMapping.INSTANCE) - .register(ConnectorHostType.COMPLEX_TYPE, QConnectorHostMapping.INSTANCE) - .register(GenericObjectType.COMPLEX_TYPE, QGenericObjectMapping.INSTANCE) - .register(LookupTableType.COMPLEX_TYPE, QLookupTableMapping.INSTANCE) - .register(LookupTableRowType.COMPLEX_TYPE, QLookupTableRowMapping.INSTANCE) - .register(NodeType.COMPLEX_TYPE, QNodeMapping.INSTANCE) - .register(ObjectType.COMPLEX_TYPE, QObjectMapping.INSTANCE) - .register(ObjectTemplateType.COMPLEX_TYPE, QObjectTemplateMapping.INSTANCE) - .register(ObjectCollectionType.COMPLEX_TYPE, QObjectCollectionMapping.INSTANCE) - .register(OperationExecutionType.COMPLEX_TYPE, QOperationExecutionMapping.INSTANCE) - .register(ReportType.COMPLEX_TYPE, QReportMapping.INSTANCE) - .register(ReportDataType.COMPLEX_TYPE, QReportDataMapping.INSTANCE) - .register(ResourceType.COMPLEX_TYPE, QResourceMapping.INSTANCE) - .register(RoleType.COMPLEX_TYPE, QRoleMapping.INSTANCE) - .register(SecurityPolicyType.COMPLEX_TYPE, QSecurityPolicyMapping.INSTANCE) - .register(SequenceType.COMPLEX_TYPE, QSequenceMapping.INSTANCE) - .register(ServiceType.COMPLEX_TYPE, QServiceMapping.INSTANCE) - .register(ShadowType.COMPLEX_TYPE, QShadowMapping.INSTANCE) + QAccessCertificationDefinitionMapping.init(repositoryContext)) + .register(ArchetypeType.COMPLEX_TYPE, QArchetypeMapping.init(repositoryContext)) + .register(AssignmentHolderType.COMPLEX_TYPE, + QAssignmentHolderMapping.init(repositoryContext)) + .register(AssignmentType.COMPLEX_TYPE, + QAssignmentMapping.initAssignment(repositoryContext)) + .register(CaseType.COMPLEX_TYPE, QCaseMapping.init(repositoryContext)) + .register(DashboardType.COMPLEX_TYPE, QDashboardMapping.init(repositoryContext)) + .register(FocusType.COMPLEX_TYPE, QFocusMapping.init(repositoryContext)) + .register(FormType.COMPLEX_TYPE, QFormMapping.init(repositoryContext)) + .register(FunctionLibraryType.COMPLEX_TYPE, + QFunctionLibraryMapping.init(repositoryContext)) + .register(ConnectorType.COMPLEX_TYPE, QConnectorMapping.init(repositoryContext)) + .register(ConnectorHostType.COMPLEX_TYPE, + QConnectorHostMapping.init(repositoryContext)) + .register(GenericObjectType.COMPLEX_TYPE, + QGenericObjectMapping.init(repositoryContext)) + .register(LookupTableType.COMPLEX_TYPE, QLookupTableMapping.init(repositoryContext)) + .register(LookupTableRowType.COMPLEX_TYPE, + QLookupTableRowMapping.init(repositoryContext)) + .register(NodeType.COMPLEX_TYPE, QNodeMapping.init(repositoryContext)) + .register(ObjectType.COMPLEX_TYPE, QObjectMapping.init(repositoryContext)) + .register(ObjectCollectionType.COMPLEX_TYPE, + QObjectCollectionMapping.init(repositoryContext)) + .register(ObjectTemplateType.COMPLEX_TYPE, + QObjectTemplateMapping.init(repositoryContext)) + .register(OperationExecutionType.COMPLEX_TYPE, + QOperationExecutionMapping.init(repositoryContext)) + .register(ReportType.COMPLEX_TYPE, QReportMapping.init(repositoryContext)) + .register(ReportDataType.COMPLEX_TYPE, QReportDataMapping.init(repositoryContext)) + .register(ResourceType.COMPLEX_TYPE, QResourceMapping.init(repositoryContext)) + .register(RoleType.COMPLEX_TYPE, QRoleMapping.init(repositoryContext)) + .register(SecurityPolicyType.COMPLEX_TYPE, + QSecurityPolicyMapping.init(repositoryContext)) + .register(SequenceType.COMPLEX_TYPE, QSequenceMapping.init(repositoryContext)) + .register(ServiceType.COMPLEX_TYPE, QServiceMapping.init(repositoryContext)) + .register(ShadowType.COMPLEX_TYPE, QShadowMapping.init(repositoryContext)) .register(SystemConfigurationType.COMPLEX_TYPE, - QSystemConfigurationMapping.INSTANCE) - .register(TaskType.COMPLEX_TYPE, QTaskMapping.INSTANCE) - .register(TriggerType.COMPLEX_TYPE, QTriggerMapping.INSTANCE) - .register(UserType.COMPLEX_TYPE, QUserMapping.INSTANCE) - .register(ValuePolicyType.COMPLEX_TYPE, QValuePolicyMapping.INSTANCE) - .register(QContainerMapping.INSTANCE) - .register(QReferenceMapping.INSTANCE) + QSystemConfigurationMapping.init(repositoryContext)) + .register(TaskType.COMPLEX_TYPE, QTaskMapping.init(repositoryContext)) + .register(TriggerType.COMPLEX_TYPE, QTriggerMapping.init(repositoryContext)) + .register(UserType.COMPLEX_TYPE, QUserMapping.init(repositoryContext)) + .register(ValuePolicyType.COMPLEX_TYPE, QValuePolicyMapping.init(repositoryContext)) + .register(QContainerMapping.initContainerMapping(repositoryContext)) + .register(QReferenceMapping.init(repositoryContext)) .seal(); - return new SqaleRepoContext(repositoryConfiguration, dataSource, mappingRegistry); + return repositoryContext; } @Bean diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java index 47dfa1f6840..8613d581234 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java @@ -54,9 +54,7 @@ /** * Repository implementation based on SQL, JDBC and Querydsl without any ORM. - * WORK IN PROGRESS: - * It will be PostgreSQL only or at least PG optimized with generic SQL support for other unsupported DB. - * Possible Oracle support is in play. + * WORK IN PROGRESS. */ public class SqaleRepositoryService implements RepositoryService { @@ -71,10 +69,9 @@ public class SqaleRepositoryService implements RepositoryService { private static final int MAX_CONFLICT_WATCHERS = 10; - private final SqaleRepoContext sqlRepoContext; + private final SqaleRepoContext repositoryContext; private final SchemaService schemaService; private final SqlQueryExecutor sqlQueryExecutor; - private final SqaleTransformerSupport transformerSupport; private final SqlPerformanceMonitorsCollection sqlPerformanceMonitorsCollection; // TODO: see comment in the SystemConfigurationChangeDispatcherImpl for related issues @@ -86,17 +83,16 @@ public class SqaleRepositoryService implements RepositoryService { private SqlPerformanceMonitorImpl performanceMonitor; // set to null in destroy public SqaleRepositoryService( - SqaleRepoContext sqlRepoContext, + SqaleRepoContext repositoryContext, SchemaService schemaService, SqlPerformanceMonitorsCollection sqlPerformanceMonitorsCollection) { - this.sqlRepoContext = sqlRepoContext; + this.repositoryContext = repositoryContext; this.schemaService = schemaService; - this.sqlQueryExecutor = new SqlQueryExecutor(sqlRepoContext); - this.transformerSupport = new SqaleTransformerSupport(schemaService, sqlRepoContext); + this.sqlQueryExecutor = new SqlQueryExecutor(repositoryContext); this.sqlPerformanceMonitorsCollection = sqlPerformanceMonitorsCollection; // monitor initialization and registration - JdbcRepositoryConfiguration config = sqlRepoContext.getJdbcRepositoryConfiguration(); + JdbcRepositoryConfiguration config = repositoryContext.getJdbcRepositoryConfiguration(); performanceMonitor = new SqlPerformanceMonitorImpl( config.getPerformanceStatisticsLevel(), config.getPerformanceStatisticsFile()); sqlPerformanceMonitorsCollection.register(performanceMonitor); @@ -125,7 +121,7 @@ public SqaleRepositoryService( try { PrismObject object; try (JdbcSession jdbcSession = - sqlRepoContext.newJdbcSession().startReadOnlyTransaction()) { + repositoryContext.newJdbcSession().startReadOnlyTransaction()) { //noinspection unchecked object = (PrismObject) readByOid(jdbcSession, type, oidUuid, options) .asPrismObject(); @@ -171,10 +167,10 @@ private S readByOid( throws SchemaException, ObjectNotFoundException { SqaleTableMapping, MObject> rootMapping = - sqlRepoContext.getMappingBySchemaType(schemaType); + repositoryContext.getMappingBySchemaType(schemaType); QObject root = rootMapping.defaultAlias(); - Tuple result = sqlRepoContext.newQuery(jdbcSession.connection()) + Tuple result = repositoryContext.newQuery(jdbcSession.connection()) .from(root) .select(rootMapping.selectExpressions(root, options)) .where(root.oid.eq(oid)) @@ -184,8 +180,7 @@ private S readByOid( throw new ObjectNotFoundException(schemaType, oid.toString()); } - return rootMapping.createTransformer(transformerSupport) - .toSchemaObject(result, root, options); + return rootMapping.toSchemaObject(result, root, options); } @Override @@ -258,7 +253,7 @@ public String addObject( // TODO use executeAttempts String oid = new AddObjectOperation<>(object, options, operationResult) - .execute(transformerSupport); + .execute(repositoryContext); invokeConflictWatchers((w) -> w.afterAddObject(oid, object)); return oid; /* @@ -359,7 +354,7 @@ public ModifyObjectResult modifyObject( logTraceModifications(modifications); // TODO: THIS is real start of modifyObjectAttempt - try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startTransaction()) { + try (JdbcSession jdbcSession = repositoryContext.newJdbcSession().startTransaction()) { RootUpdateContext, MObject> updateContext = prepareUpdateContext(jdbcSession, type, oidUuid); PrismObject prismObject = updateContext.getPrismObject(); @@ -402,10 +397,10 @@ RootUpdateContext prepareUpdateContext( throws SchemaException, ObjectNotFoundException { SqaleTableMapping, R> rootMapping = - sqlRepoContext.getMappingBySchemaType(schemaType); + repositoryContext.getMappingBySchemaType(schemaType); QObject root = rootMapping.defaultAlias(); - Tuple result = sqlRepoContext.newQuery(jdbcSession.connection()) + Tuple result = repositoryContext.newQuery(jdbcSession.connection()) .select(root.oid, root.fullObject, root.containerIdSeq) .from(root) .where(root.oid.eq(oid)) @@ -416,8 +411,7 @@ RootUpdateContext prepareUpdateContext( throw new ObjectNotFoundException(schemaType, oid.toString()); } - S object = rootMapping.createTransformer(transformerSupport) - .toSchemaObject(result, root, Collections.emptyList()); + S object = rootMapping.toSchemaObject(result, root, Collections.emptyList()); R rootRow = rootMapping.newRowObject(); rootRow.oid = oid; @@ -426,7 +420,7 @@ RootUpdateContext prepareUpdateContext( rootRow.objectType = MObjectType.fromSchemaType(object.getClass()); // we don't care about full object in row - return new RootUpdateContext<>(transformerSupport, jdbcSession, object, rootRow); + return new RootUpdateContext<>(repositoryContext, jdbcSession, object, rootRow); } private void logTraceModifications(@NotNull Collection> modifications) { @@ -463,7 +457,7 @@ private void logTraceModifications(@NotNull Collection .addParam("oid", oid) .build(); try { - try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startTransaction()) { + try (JdbcSession jdbcSession = repositoryContext.newJdbcSession().startTransaction()) { DeleteObjectResult result = deleteObjectAttempt(type, oidUuid, jdbcSession); invokeConflictWatchers((w) -> w.afterDeleteObject(oid)); @@ -484,8 +478,7 @@ private void logTraceModifications(@NotNull Collection DeleteObjectResult deleteObjectAttempt(Class type, UUID oid, JdbcSession jdbcSession) throws ObjectNotFoundException { - QueryTableMapping mapping = - transformerSupport.sqlRepoContext().getMappingBySchemaType(type); + QueryTableMapping mapping = repositoryContext.getMappingBySchemaType(type); Q entityPath = mapping.defaultAlias(); byte[] fullObject = jdbcSession.newQuery() .select(entityPath.fullObject) @@ -522,7 +515,7 @@ public int countObjects(Class type, ObjectQuery query, .build(); try { - var queryContext = SqaleQueryContext.from(type, transformerSupport, sqlRepoContext); + var queryContext = SqaleQueryContext.from(type, repositoryContext); return sqlQueryExecutor.count(queryContext, query, options); } catch (RepositoryException | RuntimeException e) { throw handledGeneralException(e, operationResult); @@ -550,7 +543,7 @@ public int countObjects(Class type, ObjectQuery query, .build(); try { - var queryContext = SqaleQueryContext.from(type, transformerSupport, sqlRepoContext); + var queryContext = SqaleQueryContext.from(type, repositoryContext); SearchResultList result = sqlQueryExecutor.list(queryContext, query, options); // TODO see the commented code from old repo lower, problems for each object must be caught @@ -569,7 +562,7 @@ public int countObjects(Class type, ObjectQuery query, /* TODO from ObjectRetriever, how to do this per-object Throwable catch + record result? - should we smuggle the OperationResult all the way to the transformer call? + should we smuggle the OperationResult all the way to the mapping call? @NotNull private List> queryResultToPrismObjects( List objects, Class type, @@ -633,7 +626,7 @@ public SearchResultList searchContainers( .build(); try { - var queryContext = SqaleQueryContext.from(type, transformerSupport, sqlRepoContext); + var queryContext = SqaleQueryContext.from(type, repositoryContext); SearchResultList result = sqlQueryExecutor.list(queryContext, query, options); return result; @@ -838,7 +831,7 @@ private SystemException handledGeneralException(@NotNull Throwable ex, Operation // TODO reconsider this whole mechanism including isFatalException decision LOGGER.error("General checked exception occurred.", ex); recordException(ex, result, - sqlRepoContext.getJdbcRepositoryConfiguration().isFatalException(ex)); + repositoryContext.getJdbcRepositoryConfiguration().isFatalException(ex)); return ex instanceof SystemException ? (SystemException) ex diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleTransformerSupport.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleTransformerSupport.java deleted file mode 100644 index 0bf18204c9b..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleTransformerSupport.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale; - -import javax.xml.namespace.QName; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.common.QUri; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.schema.SchemaService; -import com.evolveum.midpoint.util.QNameUtil; - -/** - * Extension of {@link SqlTransformerSupport} adding Sqale features like {@link UriCache} support. - */ -public class SqaleTransformerSupport extends SqlTransformerSupport { - - public SqaleTransformerSupport(SchemaService schemaService, SqaleRepoContext sqaleRepoContext) { - super(schemaService, sqaleRepoContext); - } - - private SqaleRepoContext sqaleRepoContext() { - return (SqaleRepoContext) sqlRepoContext; - } - - /** - * Returns ID for relation QName or {@link UriCache#UNKNOWN_ID} without going to the database. - * Relation is normalized before consulting {@link UriCache}. - * Never returns null; returns default ID for configured default relation if provided with null. - */ - public @NotNull Integer searchCachedRelationId(QName qName) { - return sqaleRepoContext().searchCachedUriId(QNameUtil.qNameToUri(normalizeRelation(qName))); - } - - /** Returns ID for cached URI without going to the database. */ - public Integer resolveUriToId(String uri) { - return sqaleRepoContext().resolveUriToId(uri); - } - - /** Returns ID for cached URI without going to the database. */ - public Integer resolveUriToId(QName uri) { - return sqaleRepoContext().resolveUriToId(uri); - } - - /** - * Returns ID for relation QName creating new {@link QUri} row in DB as needed. - * Relation is normalized before consulting the cache. - * Never returns null, returns default ID for configured default relation. - */ - public Integer processCacheableRelation(QName qName) { - return processCacheableUri( - QNameUtil.qNameToUri(normalizeRelation(qName))); - } - - /** Returns ID for URI creating new cache row in DB as needed. */ - public Integer processCacheableUri(String uri) { - return sqaleRepoContext().processCacheableUri(uri); - } -} - diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefItemDeltaProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefItemDeltaProcessor.java index c31601cc86c..a9256c5d88d 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefItemDeltaProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefItemDeltaProcessor.java @@ -57,7 +57,8 @@ , R> RefItemDeltaProcessor( public void setValue(Referencable value) { context.set(oidPath, UUID.fromString(value.getOid())); context.set(typePath, MObjectType.fromTypeQName(value.getType())); - context.set(relationIdPath, context.processCacheableRelation(value.getRelation())); + context.set(relationIdPath, + context.repositoryContext().processCacheableRelation(value.getRelation())); } @Override diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefTableItemDeltaProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefTableItemDeltaProcessor.java index 011dbb6b2d3..2b106886e84 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefTableItemDeltaProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/RefTableItemDeltaProcessor.java @@ -46,7 +46,7 @@ public void addValues(Collection values) { public void deleteValues(Collection values) { Q r = refTableMapping.defaultAlias(); for (Referencable ref : values) { - Integer relId = context.transformerSupport().searchCachedRelationId(ref.getRelation()); + Integer relId = context.repositoryContext().searchCachedRelationId(ref.getRelation()); context.jdbcSession().newDelete(r) .where(r.isOwnedBy(context.row()) .and(r.targetOid.eq(UUID.fromString(ref.getOid()))) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/UriItemDeltaProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/UriItemDeltaProcessor.java index c1acaf69af4..00e02ed2c52 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/UriItemDeltaProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/UriItemDeltaProcessor.java @@ -29,6 +29,6 @@ public , R> UriItemDeltaProcessor( @Override public @Nullable Integer convertRealValue(Object realValue) { - return context.processCacheableUri((String) realValue); + return context.repositoryContext().processCacheableUri((String) realValue); } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/UriItemFilterProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/UriItemFilterProcessor.java index b154daed1fc..2125d540458 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/UriItemFilterProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/UriItemFilterProcessor.java @@ -36,6 +36,6 @@ public , R> UriItemFilterProcessor( public Predicate process(PropertyValueFilter filter) throws QueryException { return createBinaryCondition(filter, path, ValueFilterValues.from(filter, - u -> ((SqaleRepoContext) context.sqlRepoContext()).searchCachedUriId(u))); + u -> ((SqaleRepoContext) context.repositoryContext()).searchCachedUriId(u))); } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/operations/AddObjectOperation.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/operations/AddObjectOperation.java index 4e07ff7847e..c0614196cad 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/operations/AddObjectOperation.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/operations/AddObjectOperation.java @@ -18,12 +18,11 @@ import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.repo.api.RepoAddOptions; import com.evolveum.midpoint.repo.sqale.ContainerValueIdGenerator; -import com.evolveum.midpoint.repo.sqale.SqaleTransformerSupport; -import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping; +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; import com.evolveum.midpoint.schema.result.OperationResult; @@ -47,9 +46,9 @@ public class AddObjectOperation, R ex private final RepoAddOptions options; private final OperationResult result; - private SqlRepoContext sqlRepoContext; + private SqlRepoContext repositoryContext; private Q root; - private ObjectSqlTransformer transformer; + private QObjectMapping rootMapping; private MObjectType objectType; public AddObjectOperation(@NotNull PrismObject object, @@ -59,19 +58,18 @@ public AddObjectOperation(@NotNull PrismObject object, this.result = result; } - /** Inserts the object provided to the constructor and returns its OID. */ - public String execute(SqaleTransformerSupport transformerSupport) + /** + * Inserts the object provided to the constructor and returns its OID. + */ + public String execute(SqaleRepoContext repositoryContext) throws SchemaException, ObjectAlreadyExistsException { try { // TODO utilize options and result - sqlRepoContext = transformerSupport.sqlRepoContext(); + this.repositoryContext = repositoryContext; Class schemaObjectClass = object.getCompileTimeClass(); objectType = MObjectType.fromSchemaType(schemaObjectClass); - SqaleTableMapping rootMapping = - sqlRepoContext.getMappingBySchemaType(schemaObjectClass); + rootMapping = repositoryContext.getMappingBySchemaType(schemaObjectClass); root = rootMapping.defaultAlias(); - transformer = (ObjectSqlTransformer) - rootMapping.createTransformer(transformerSupport); // we don't want CID generation here, because overwrite works different then normal add @@ -98,11 +96,11 @@ private String overwriteObject() { private String addObjectWithOid() throws SchemaException { long lastCid = new ContainerValueIdGenerator().generateForNewObject(object); - try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startTransaction()) { + try (JdbcSession jdbcSession = repositoryContext.newJdbcSession().startTransaction()) { S schemaObject = object.asObjectable(); - R row = transformer.toRowObjectWithoutFullObject(schemaObject, jdbcSession); + R row = rootMapping.toRowObjectWithoutFullObject(schemaObject, jdbcSession); row.containerIdSeq = lastCid + 1; - transformer.setFullObject(row, schemaObject); + rootMapping.setFullObject(row, schemaObject); UUID oid = jdbcSession.newInsert(root) // default populate mapper ignores null, that's good, especially for objectType @@ -110,7 +108,7 @@ private String addObjectWithOid() throws SchemaException { .executeWithKey(root.oid); row.objectType = objectType; // sub-entities can use it, now it's safe to set it - transformer.storeRelatedEntities(row, schemaObject, jdbcSession); + rootMapping.storeRelatedEntities(row, schemaObject, jdbcSession); jdbcSession.commit(); return Objects.requireNonNull(oid, "OID of inserted object can't be null") @@ -119,9 +117,9 @@ private String addObjectWithOid() throws SchemaException { } private String addObjectWithoutOid() throws SchemaException { - try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startTransaction()) { + try (JdbcSession jdbcSession = repositoryContext.newJdbcSession().startTransaction()) { S schemaObject = object.asObjectable(); - R row = transformer.toRowObjectWithoutFullObject(schemaObject, jdbcSession); + R row = rootMapping.toRowObjectWithoutFullObject(schemaObject, jdbcSession); // first insert without full object, because we don't know the OID yet UUID oid = jdbcSession.newInsert(root) @@ -136,7 +134,7 @@ private String addObjectWithoutOid() throws SchemaException { long lastCid = new ContainerValueIdGenerator().generateForNewObject(object); // now to update full object with known OID - transformer.setFullObject(row, schemaObject); + rootMapping.setFullObject(row, schemaObject); jdbcSession.newUpdate(root) .set(root.fullObject, row.fullObject) .set(root.containerIdSeq, lastCid + 1) @@ -145,7 +143,7 @@ private String addObjectWithoutOid() throws SchemaException { row.oid = oid; row.objectType = objectType; // sub-entities can use it, now it's safe to set it - transformer.storeRelatedEntities(row, schemaObject, jdbcSession); + rootMapping.storeRelatedEntities(row, schemaObject, jdbcSession); jdbcSession.commit(); return oidString; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ObjectTemplateSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ObjectTemplateSqlTransformer.java deleted file mode 100644 index d63494567a3..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ObjectTemplateSqlTransformer.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateType; - -public class ObjectTemplateSqlTransformer - extends ObjectSqlTransformer { - - private final QObjectTemplateMapping mapping; - - public ObjectTemplateSqlTransformer( - SqlTransformerSupport transformerSupport, QObjectTemplateMapping mapping) { - super(transformerSupport, mapping); - this.mapping = mapping; - } - - @Override - public void storeRelatedEntities(@NotNull MObject row, - @NotNull ObjectTemplateType schemaObject, @NotNull JdbcSession jdbcSession) { - super.storeRelatedEntities(row, schemaObject, jdbcSession); - - storeRefs(row, schemaObject.getIncludeRef(), - QObjectReferenceMapping.INSTANCE_INCLUDE, jdbcSession); - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplateMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplateMapping.java index 674c523ccd8..7bf93c339ee 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplateMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplateMapping.java @@ -8,10 +8,13 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateType.F_INCLUDE_REF; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateType; /** @@ -22,13 +25,15 @@ public class QObjectTemplateMapping public static final String DEFAULT_ALIAS_NAME = "ot"; - public static final QObjectTemplateMapping INSTANCE = new QObjectTemplateMapping(); + public static QObjectTemplateMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QObjectTemplateMapping(repositoryContext); + } - private QObjectTemplateMapping() { + private QObjectTemplateMapping(@NotNull SqaleRepoContext repositoryContext) { super(QObjectTemplate.TABLE_NAME, DEFAULT_ALIAS_NAME, - ObjectTemplateType.class, QObjectTemplate.class); + ObjectTemplateType.class, QObjectTemplate.class, repositoryContext); - addRefMapping(F_INCLUDE_REF, QObjectReferenceMapping.INSTANCE_INCLUDE); + addRefMapping(F_INCLUDE_REF, QObjectReferenceMapping.initForInclude(repositoryContext)); } @Override @@ -37,13 +42,16 @@ protected QObjectTemplate newAliasInstance(String alias) { } @Override - public ObjectTemplateSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new ObjectTemplateSqlTransformer(transformerSupport, this); + public MObject newRowObject() { + return new MObject(); } @Override - public MObject newRowObject() { - return new MObject(); + public void storeRelatedEntities(@NotNull MObject row, + @NotNull ObjectTemplateType schemaObject, @NotNull JdbcSession jdbcSession) { + super.storeRelatedEntities(row, schemaObject, jdbcSession); + + storeRefs(row, schemaObject.getIncludeRef(), + QObjectReferenceMapping.getForInclude(), jdbcSession); } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QOwnedByMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QOwnedByMapping.java index b9b1c1ec9db..6345dca8ff4 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QOwnedByMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QOwnedByMapping.java @@ -6,7 +6,7 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; /** * Marks mappings for {@link QOwnedBy} entities. @@ -21,5 +21,5 @@ public interface QOwnedByMapping { /** Returns a row with foreign key fields referencing the provided owner row. */ R newRowObject(OR ownerRow); - TransformerForOwnedBy createTransformer(SqlTransformerSupport transformerSupport); + R insert(S schemaObject, OR ownerRow, JdbcSession jdbcSession); } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleMappingMixin.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleMappingMixin.java index f9f7a8fdf78..dc005cf2743 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleMappingMixin.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleMappingMixin.java @@ -1,5 +1,6 @@ package com.evolveum.midpoint.repo.sqale.qmodel; +import java.util.Objects; import java.util.function.BiFunction; import javax.xml.namespace.QName; @@ -65,6 +66,7 @@ default SqaleNestedMapping addNestedMapping( /** Defines reference mapping for both query and modifications. */ default SqaleMappingMixin addRefMapping( @NotNull QName itemName, @NotNull QReferenceMapping referenceMapping) { + Objects.requireNonNull(referenceMapping, "referenceMapping"); addItemMapping(itemName, new SqaleItemSqlMapper<>( ctx -> new RefTableItemFilterProcessor<>(ctx, referenceMapping), ctx -> new RefTableItemDeltaProcessor<>(ctx, referenceMapping))); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java index 81b5cbdb34b..d5d836e7fb0 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java @@ -6,29 +6,63 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; import java.util.function.Function; +import javax.xml.namespace.QName; +import com.querydsl.core.Tuple; import com.querydsl.core.types.dsl.*; +import com.querydsl.sql.ColumnMetadata; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.delta.item.*; import com.evolveum.midpoint.repo.sqale.filtering.RefItemFilterProcessor; import com.evolveum.midpoint.repo.sqale.filtering.UriItemFilterProcessor; import com.evolveum.midpoint.repo.sqale.mapping.SqaleItemSqlMapper; +import com.evolveum.midpoint.repo.sqale.qmodel.common.QUri; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.ref.MReference; +import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReferenceMapping; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.repo.sqlbase.filtering.item.EnumItemFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.item.PolyStringItemFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.item.SimpleItemFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.item.TimestampItemFilterProcessor; +import com.evolveum.midpoint.repo.sqlbase.mapping.QueryModelMappingRegistry; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.SelectorOptions; +import com.evolveum.midpoint.util.MiscUtil; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; /** * Mapping superclass with common functions for {@link QObject} and non-objects (e.g. containers). * See javadoc in {@link QueryTableMapping} for more. * + * Mappings are often initialized using static `init*(repositoryContext)` methods, various + * suffixes are used for these reasons: + * + * * To differentiate various instances for the same mapping type, e.g. various references + * stored in separate tables; + * * or to avoid return type clash of the `init` method, even though they are static + * and technically independent Java meddles too much. + * + * Some subclasses (typically containers and refs) track their instances and avoid unnecessary + * instance creation for the same init method; the instance is available via matching `get*()`. + * Other subclasses (most objects) don't have `get()` methods but can be obtained using + * {@link QueryModelMappingRegistry#getByQueryType(Class)}, or by schema type (e.g. in tests). + * * @param schema type * @param type of entity path * @param row type related to the {@link Q} @@ -42,8 +76,13 @@ protected SqaleTableMapping( @NotNull String tableName, @NotNull String defaultAliasName, @NotNull Class schemaType, - @NotNull Class queryType) { - super(tableName, defaultAliasName, schemaType, queryType); + @NotNull Class queryType, + @NotNull SqaleRepoContext repositoryContext) { + super(tableName, defaultAliasName, schemaType, queryType, repositoryContext); + } + + public SqaleRepoContext repositoryContext() { + return (SqaleRepoContext) super.repositoryContext(); } /** @@ -80,7 +119,8 @@ public SqaleItemSqlMapper integerMapper( * @param mapped schema type, see javadoc in {@link QueryTableMapping} */ @Override - protected SqaleItemSqlMapper booleanMapper(Function rootToQueryItem) { + protected SqaleItemSqlMapper booleanMapper( + Function rootToQueryItem) { return new SqaleItemSqlMapper<>( ctx -> new SimpleItemFilterProcessor<>(ctx, rootToQueryItem), ctx -> new SimpleItemDeltaProcessor<>(ctx, rootToQueryItem), @@ -169,4 +209,147 @@ public > SqaleItemSqlMapper enumMapper( ctx -> new EnumItemDeltaProcessor<>(ctx, rootToQueryItem), rootToQueryItem); } + + @Override + public S toSchemaObject(R row) { + throw new UnsupportedOperationException("Use toSchemaObject(Tuple,...)"); + } + + /** + * Transforms row Tuple containing {@link R} under entity path and extension columns. + */ + @Override + public S toSchemaObject(Tuple tuple, Q entityPath, + Collection> options) + throws SchemaException { + S schemaObject = toSchemaObject(tuple.get(entityPath)); + processExtensionColumns(schemaObject, tuple, entityPath); + return schemaObject; + } + + @SuppressWarnings("unused") + protected void processExtensionColumns(S schemaObject, Tuple tuple, Q entityPath) { + // empty by default, can be overridden + } + + /** + * Returns {@link ObjectReferenceType} with specified oid, proper type based on + * {@link MObjectType} and, optionally, target name/description. + * Returns {@code null} if OID is null. + * Fails if OID is not null and {@code repoObjectType} is null. + */ + @Nullable + protected ObjectReferenceType objectReferenceType( + @Nullable String oid, MObjectType repoObjectType, String targetName) { + if (oid == null) { + return null; + } + if (repoObjectType == null) { + throw new IllegalArgumentException( + "NULL object type provided for object reference with OID " + oid); + } + + return new ObjectReferenceType() + .oid(oid) + .type(repositoryContext().schemaClassToQName(repoObjectType.getSchemaType())) + .description(targetName) + .targetName(targetName); + } + + /** + * Trimming the value to the column size from column metadata (must be specified). + */ + protected @Nullable String trim( + @Nullable String value, @NotNull ColumnMetadata columnMetadata) { + if (!columnMetadata.hasSize()) { + throw new IllegalArgumentException( + "trimString with column metadata without specified size: " + columnMetadata); + } + return MiscUtil.trimString(value, columnMetadata.getSize()); + } + + /** + * Returns ID for relation QName creating new {@link QUri} row in DB as needed. + * Relation is normalized before consulting the cache. + * Never returns null, returns default ID for configured default relation. + */ + protected Integer processCacheableRelation(QName qName) { + return repositoryContext().processCacheableRelation(qName); + } + + /** Returns ID for URI creating new cache row in DB as needed. */ + protected Integer processCacheableUri(String uri) { + return uri != null + ? repositoryContext().processCacheableUri(uri) + : null; + } + + /** Returns ID for URI creating new cache row in DB as needed. */ + protected Integer processCacheableUri(QName qName) { + return qName != null + ? repositoryContext().processCacheableUri(QNameUtil.qNameToUri(qName)) + : null; + } + + /** + * Returns IDs as Integer array for URI strings creating new cache row in DB as needed. + * Returns null for null or empty list on input. + */ + protected Integer[] processCacheableUris(List uris) { + if (uris == null || uris.isEmpty()) { + return null; + } + return uris.stream() + .map(uri -> processCacheableUri(uri)) + .toArray(Integer[]::new); + } + + protected @Nullable UUID oidToUUid(@Nullable String oid) { + return oid != null ? UUID.fromString(oid) : null; + } + + protected MObjectType schemaTypeToObjectType(QName schemaType) { + return schemaType == null ? null : + MObjectType.fromSchemaType(repositoryContext().qNameToSchemaClass(schemaType)); + } + + protected void setPolyString(PolyStringType polyString, + Consumer origConsumer, Consumer normConsumer) { + if (polyString != null) { + origConsumer.accept(polyString.getOrig()); + normConsumer.accept(polyString.getNorm()); + } + } + + protected void setReference(ObjectReferenceType ref, + Consumer targetOidConsumer, Consumer targetTypeConsumer, + Consumer relationIdConsumer) { + if (ref != null) { + targetOidConsumer.accept(oidToUUid(ref.getOid())); + targetTypeConsumer.accept(schemaTypeToObjectType(ref.getType())); + relationIdConsumer.accept(processCacheableRelation(ref.getRelation())); + } + } + + protected , OR> void storeRefs( + @NotNull OR ownerRow, @NotNull List refs, + @NotNull QReferenceMapping mapping, @NotNull JdbcSession jdbcSession) { + if (!refs.isEmpty()) { + refs.forEach(ref -> mapping.insert(ref, ownerRow, jdbcSession)); + } + } + + protected String[] arrayFor(List strings) { + if (strings == null || strings.isEmpty()) { + return null; + } + return strings.toArray(String[]::new); + } + + /** Convenient insert shortcut when the row is fully populated. */ + protected void insert(R row, JdbcSession jdbcSession) { + jdbcSession.newInsert(defaultAlias()) + .populate(row) + .execute(); + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTransformerBase.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTransformerBase.java deleted file mode 100644 index 9e86853cf9e..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTransformerBase.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel; - -import java.util.Collection; -import java.util.List; -import java.util.UUID; -import java.util.function.Consumer; -import javax.xml.namespace.QName; - -import com.querydsl.core.Tuple; -import com.querydsl.sql.ColumnMetadata; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.evolveum.midpoint.repo.sqale.SqaleTransformerSupport; -import com.evolveum.midpoint.repo.sqale.qmodel.common.QUri; -import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.MReference; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReferenceMapping; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.ReferenceSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; -import com.evolveum.midpoint.repo.sqlbase.mapping.SqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.SelectorOptions; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; -import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; - -/** - * @param schema type - * @param type of entity path - * @param type of the row bean for the table - */ -public abstract class SqaleTransformerBase, R> - implements SqlTransformer { - - protected final Logger logger = LoggerFactory.getLogger(getClass()); - - protected final SqaleTransformerSupport transformerSupport; - - /** - * Constructor uses {@link SqlTransformerSupport} type even when it really is - * {@link SqaleTransformerSupport}, but this way we can cast it just once here; otherwise cast - * would be needed in each implementation of {@link QueryTableMapping#createTransformer)}. - * (Alternative is to parametrize {@link QueryTableMapping} with various {@link SqlTransformer} - * types which is not convenient at all. This little downcast is low price to pay.) - */ - protected SqaleTransformerBase(SqlTransformerSupport transformerSupport) { - this.transformerSupport = (SqaleTransformerSupport) transformerSupport; - } - - protected abstract QueryTableMapping mapping(); - - @Override - public S toSchemaObject(R row) { - throw new UnsupportedOperationException("Use toSchemaObject(Tuple,...)"); - } - - /** - * Transforms row Tuple containing {@link R} under entity path and extension columns. - */ - @Override - public S toSchemaObject(Tuple tuple, Q entityPath, - Collection> options) - throws SchemaException { - S schemaObject = toSchemaObject(tuple.get(entityPath)); - processExtensionColumns(schemaObject, tuple, entityPath); - return schemaObject; - } - - @SuppressWarnings("unused") - protected void processExtensionColumns(S schemaObject, Tuple tuple, Q entityPath) { - // empty by default, can be overridden - } - - /** - * Returns {@link ObjectReferenceType} with specified oid, proper type based on - * {@link MObjectType} and, optionally, target name/description. - * Returns {@code null} if OID is null. - * Fails if OID is not null and {@code repoObjectType} is null. - */ - @Nullable - protected ObjectReferenceType objectReferenceType( - @Nullable String oid, MObjectType repoObjectType, String targetName) { - if (oid == null) { - return null; - } - if (repoObjectType == null) { - throw new IllegalArgumentException( - "NULL object type provided for object reference with OID " + oid); - } - - return new ObjectReferenceType() - .oid(oid) - .type(transformerSupport.schemaClassToQName(repoObjectType.getSchemaType())) - .description(targetName) - .targetName(targetName); - } - - /** - * Trimming the value to the column size from column metadata (must be specified). - */ - protected @Nullable String trim( - @Nullable String value, @NotNull ColumnMetadata columnMetadata) { - if (!columnMetadata.hasSize()) { - throw new IllegalArgumentException( - "trimString with column metadata without specified size: " + columnMetadata); - } - return MiscUtil.trimString(value, columnMetadata.getSize()); - } - - protected @NotNull Integer searchCachedRelationId(QName qName) { - return transformerSupport.searchCachedRelationId(qName); - } - - /** Returns ID for cached URI without going to the database. */ - protected Integer resolveUriToId(String uri) { - return transformerSupport.resolveUriToId(uri); - } - - /** - * Returns ID for relation QName creating new {@link QUri} row in DB as needed. - * Relation is normalized before consulting the cache. - * Never returns null, returns default ID for configured default relation. - */ - protected Integer processCacheableRelation(QName qName) { - return transformerSupport.processCacheableRelation(qName); - } - - /** Returns ID for URI creating new cache row in DB as needed. */ - protected Integer processCacheableUri(String uri) { - return uri != null - ? transformerSupport.processCacheableUri(uri) - : null; - } - - /** Returns ID for URI creating new cache row in DB as needed. */ - protected Integer processCacheableUri(QName qName) { - return qName != null - ? transformerSupport.processCacheableUri(QNameUtil.qNameToUri(qName)) - : null; - } - - /** - * Returns IDs as Integer array for URI strings creating new cache row in DB as needed. - * Returns null for null or empty list on input. - */ - protected Integer[] processCacheableUris(List uris) { - if (uris == null || uris.isEmpty()) { - return null; - } - return uris.stream() - .map(uri -> processCacheableUri(uri)) - .toArray(Integer[]::new); - } - - protected @Nullable UUID oidToUUid(@Nullable String oid) { - return oid != null ? UUID.fromString(oid) : null; - } - - protected MObjectType schemaTypeToObjectType(QName schemaType) { - return schemaType == null ? null : - MObjectType.fromSchemaType( - transformerSupport.qNameToSchemaClass(schemaType)); - } - - protected void setPolyString(PolyStringType polyString, - Consumer origConsumer, Consumer normConsumer) { - if (polyString != null) { - origConsumer.accept(polyString.getOrig()); - normConsumer.accept(polyString.getNorm()); - } - } - - protected void setReference(ObjectReferenceType ref, - Consumer targetOidConsumer, Consumer targetTypeConsumer, - Consumer relationIdConsumer) { - if (ref != null) { - targetOidConsumer.accept(oidToUUid(ref.getOid())); - targetTypeConsumer.accept(schemaTypeToObjectType(ref.getType())); - relationIdConsumer.accept(processCacheableRelation(ref.getRelation())); - } - } - - protected , OR> void storeRefs( - @NotNull OR ownerRow, @NotNull List refs, - @NotNull QReferenceMapping mapping, @NotNull JdbcSession jdbcSession) { - if (!refs.isEmpty()) { - ReferenceSqlTransformer transformer = - mapping.createTransformer(transformerSupport); - refs.forEach(ref -> transformer.insert(ref, ownerRow, jdbcSession)); - } - } - - protected String[] arrayFor(List strings) { - if (strings == null || strings.isEmpty()) { - return null; - } - return strings.toArray(String[]::new); - } - - /** Convenient insert shortcut when the row is fully populated. */ - protected void insert(R row, JdbcSession jdbcSession) { - jdbcSession.newInsert(mapping().defaultAlias()) - .populate(row) - .execute(); - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/TransformerForOwnedBy.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/TransformerForOwnedBy.java deleted file mode 100644 index b760c78d907..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/TransformerForOwnedBy.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel; - -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; - -/** - * Declares capability to insert row for the provided schema object owned by provided row. - * - * @param schema type - * @param target type of the transformation, a row bean - * @param row type of the reference owner - */ -public interface TransformerForOwnedBy { - - /** Contract for insertion of row of type {@link R} owned by {@link OR}. */ - R insert(S schemaObject, OR ownerRow, JdbcSession jdbcSession); -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/AccessCertificationDefinitionSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/AccessCertificationDefinitionSqlTransformer.java deleted file mode 100644 index 52570a81a0d..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/AccessCertificationDefinitionSqlTransformer.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.accesscert; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationDefinitionType; - -public class AccessCertificationDefinitionSqlTransformer - extends ObjectSqlTransformer { - - public AccessCertificationDefinitionSqlTransformer( - SqlTransformerSupport transformerSupport, - QAccessCertificationDefinitionMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MAccessCertificationDefinition toRowObjectWithoutFullObject( - AccessCertificationDefinitionType schemaObject, JdbcSession jdbcSession) { - MAccessCertificationDefinition row = - super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); - - row.handlerUriId = processCacheableUri(schemaObject.getHandlerUri()); - row.lastCampaignStartedTimestamp = - MiscUtil.asInstant(schemaObject.getLastCampaignStartedTimestamp()); - row.lastCampaignClosedTimestamp = - MiscUtil.asInstant(schemaObject.getLastCampaignClosedTimestamp()); - setReference(schemaObject.getOwnerRef(), - o -> row.ownerRefTargetOid = o, - t -> row.ownerRefTargetType = t, - r -> row.ownerRefRelationId = r); - - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinitionMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinitionMapping.java index e302547d70f..0c782c0d664 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinitionMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinitionMapping.java @@ -8,8 +8,12 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractAccessCertificationDefinitionType.*; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationDefinitionType; /** @@ -22,12 +26,15 @@ public class QAccessCertificationDefinitionMapping public static final String DEFAULT_ALIAS_NAME = "acd"; - public static final QAccessCertificationDefinitionMapping INSTANCE = - new QAccessCertificationDefinitionMapping(); + public static QAccessCertificationDefinitionMapping init( + @NotNull SqaleRepoContext repositoryContext) { + return new QAccessCertificationDefinitionMapping(repositoryContext); + } - private QAccessCertificationDefinitionMapping() { + private QAccessCertificationDefinitionMapping(@NotNull SqaleRepoContext repositoryContext) { super(QAccessCertificationDefinition.TABLE_NAME, DEFAULT_ALIAS_NAME, - AccessCertificationDefinitionType.class, QAccessCertificationDefinition.class); + AccessCertificationDefinitionType.class, QAccessCertificationDefinition.class, + repositoryContext); addItemMapping(F_HANDLER_URI, uriMapper(q -> q.handlerUriId)); addItemMapping(F_LAST_CAMPAIGN_STARTED_TIMESTAMP, @@ -46,12 +53,26 @@ protected QAccessCertificationDefinition newAliasInstance(String alias) { } @Override - public AccessCertificationDefinitionSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new AccessCertificationDefinitionSqlTransformer(transformerSupport, this); + public MAccessCertificationDefinition newRowObject() { + return new MAccessCertificationDefinition(); } @Override - public MAccessCertificationDefinition newRowObject() { - return new MAccessCertificationDefinition(); + public @NotNull MAccessCertificationDefinition toRowObjectWithoutFullObject( + AccessCertificationDefinitionType schemaObject, JdbcSession jdbcSession) { + MAccessCertificationDefinition row = + super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); + + row.handlerUriId = processCacheableUri(schemaObject.getHandlerUri()); + row.lastCampaignStartedTimestamp = + MiscUtil.asInstant(schemaObject.getLastCampaignStartedTimestamp()); + row.lastCampaignClosedTimestamp = + MiscUtil.asInstant(schemaObject.getLastCampaignClosedTimestamp()); + setReference(schemaObject.getOwnerRef(), + o -> row.ownerRefTargetOid = o, + t -> row.ownerRefTargetType = t, + r -> row.ownerRefRelationId = r); + + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/AssignmentSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/AssignmentSqlTransformer.java deleted file mode 100644 index e69e2d1fab4..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/AssignmentSqlTransformer.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.assignment; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ContainerSqlTransformer; -import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstructionType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; - -public class AssignmentSqlTransformer - extends ContainerSqlTransformer, MAssignment, OR> { - - public AssignmentSqlTransformer( - SqlTransformerSupport transformerSupport, QAssignmentMapping mapping) { - super(transformerSupport, mapping); - } - - // about duplication see the comment in ObjectSqlTransformer.toRowObjectWithoutFullObject - @SuppressWarnings("DuplicatedCode") - @Override - public MAssignment insert(AssignmentType assignment, OR ownerRow, JdbcSession jdbcSession) { - MAssignment row = initRowObject(assignment, ownerRow); - - row.ownerType = ownerRow.objectType; - row.lifecycleState = assignment.getLifecycleState(); - row.orderValue = assignment.getOrder(); - setReference(assignment.getOrgRef(), - o -> row.orgRefTargetOid = o, - t -> row.orgRefTargetType = t, - r -> row.orgRefRelationId = r); - setReference(assignment.getTargetRef(), - o -> row.targetRefTargetOid = o, - t -> row.targetRefTargetType = t, - r -> row.targetRefRelationId = r); - setReference(assignment.getTenantRef(), - o -> row.tenantRefTargetOid = o, - t -> row.tenantRefTargetType = t, - r -> row.tenantRefRelationId = r); - - // TODO no idea how to do this yet, somehow related to RAssignment.extension -// row.extId = assignment.getExtension()...id?; -// row.extOid =; - row.policySituations = processCacheableUris(assignment.getPolicySituation()); - // TODO extensions stored inline (JSON) - - ConstructionType construction = assignment.getConstruction(); - if (construction != null) { - setReference(construction.getResourceRef(), - o -> row.resourceRefTargetOid = o, - t -> row.resourceRefTargetType = t, - r -> row.resourceRefRelationId = r); - } - - ActivationType activation = assignment.getActivation(); - if (activation != null) { - row.administrativeStatus = activation.getAdministrativeStatus(); - row.effectiveStatus = activation.getEffectiveStatus(); - row.enableTimestamp = MiscUtil.asInstant(activation.getEnableTimestamp()); - row.disableTimestamp = MiscUtil.asInstant(activation.getDisableTimestamp()); - row.disableReason = activation.getDisableReason(); - row.validityStatus = activation.getValidityStatus(); - row.validFrom = MiscUtil.asInstant(activation.getValidFrom()); - row.validTo = MiscUtil.asInstant(activation.getValidTo()); - row.validityChangeTimestamp = MiscUtil.asInstant(activation.getValidityChangeTimestamp()); - row.archiveTimestamp = MiscUtil.asInstant(activation.getArchiveTimestamp()); - } - - MetadataType metadata = assignment.getMetadata(); - if (metadata != null) { - setReference(metadata.getCreatorRef(), - o -> row.creatorRefTargetOid = o, - t -> row.creatorRefTargetType = t, - r -> row.creatorRefRelationId = r); - row.createChannelId = processCacheableUri(metadata.getCreateChannel()); - row.createTimestamp = MiscUtil.asInstant(metadata.getCreateTimestamp()); - - setReference(metadata.getModifierRef(), - o -> row.modifierRefTargetOid = o, - t -> row.modifierRefTargetType = t, - r -> row.modifierRefRelationId = r); - row.modifyChannelId = processCacheableUri(metadata.getModifyChannel()); - row.modifyTimestamp = MiscUtil.asInstant(metadata.getModifyTimestamp()); - } - - // insert before treating sub-entities - insert(row, jdbcSession); - - if (metadata != null) { - storeRefs(row, metadata.getCreateApproverRef(), - QAssignmentReferenceMapping.INSTANCE_ASSIGNMENT_CREATE_APPROVER, jdbcSession); - storeRefs(row, metadata.getModifyApproverRef(), - QAssignmentReferenceMapping.INSTANCE_ASSIGNMENT_MODIFY_APPROVER, jdbcSession); - } - - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java index 35efcf9ae88..b66cda08b24 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java @@ -8,10 +8,16 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType.*; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; +import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainerMapping; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReferenceMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstructionType; @@ -19,6 +25,10 @@ /** * Mapping between {@link QAssignment} and {@link AssignmentType}. + * There are separate instances for assignments and inducements and the instance also knows + * the {@link MAssignment#containerType} it should set. + * Only the instance for assignments is registered for queries as there is no way to distinguish + * between assignments and inducements when searching containers in the Query API anyway. * * @param type of the owner row */ @@ -27,13 +37,50 @@ public class QAssignmentMapping public static final String DEFAULT_ALIAS_NAME = "a"; - public static final QAssignmentMapping INSTANCE = new QAssignmentMapping<>(); + /** Default assignment mapping instance, for queries it works for inducements too. */ + private static QAssignmentMapping instanceAssignment; + + /** Inducement mapping instance, this must be used for inserting inducements. */ + private static QAssignmentMapping instanceInducement; + + public static QAssignmentMapping + initAssignment(@NotNull SqaleRepoContext repositoryContext) { + if (instanceAssignment == null) { + instanceAssignment = new QAssignmentMapping<>( + MContainerType.ASSIGNMENT, repositoryContext); + } + return getAssignment(); + } + + public static QAssignmentMapping getAssignment() { + //noinspection unchecked + return (QAssignmentMapping) Objects.requireNonNull(instanceAssignment); + } + + public static QAssignmentMapping + initInducement(@NotNull SqaleRepoContext repositoryContext) { + if (instanceInducement == null) { + instanceInducement = new QAssignmentMapping<>( + MContainerType.INDUCEMENT, repositoryContext); + } + return getInducement(); + } + + public static QAssignmentMapping getInducement() { + //noinspection unchecked + return (QAssignmentMapping) Objects.requireNonNull(instanceInducement); + } + + private final MContainerType containerType; // We can't declare Class>.class, so we cheat a bit. @SuppressWarnings({ "unchecked", "rawtypes" }) - private QAssignmentMapping() { + private QAssignmentMapping( + @NotNull MContainerType containerType, + @NotNull SqaleRepoContext repositoryContext) { super(QAssignment.TABLE_NAME, DEFAULT_ALIAS_NAME, - AssignmentType.class, (Class) QAssignment.class); + AssignmentType.class, (Class) QAssignment.class, repositoryContext); + this.containerType = containerType; // TODO OWNER_TYPE is new thing and can help avoid join to concrete object table // But this will likely require special treatment/heuristic. @@ -96,17 +143,12 @@ private QAssignmentMapping() { uriMapper(q -> q.modifyChannelId)) .addItemMapping(MetadataType.F_MODIFY_TIMESTAMP, timestampMapper(q -> q.modifyTimestamp)) - .addRefMapping(MetadataType.F_CREATE_APPROVER_REF, referenceMapping( - QAssignmentReferenceMapping.INSTANCE_ASSIGNMENT_CREATE_APPROVER)) - .addRefMapping(MetadataType.F_MODIFY_APPROVER_REF, referenceMapping( - QAssignmentReferenceMapping.INSTANCE_ASSIGNMENT_MODIFY_APPROVER)); - } - - /** Fixes rigid parametric types of static mapping instance to this instance. */ - private QReferenceMapping, MAssignment> referenceMapping( - QAssignmentReferenceMapping referenceMapping) { - //noinspection unchecked - return (QAssignmentReferenceMapping) referenceMapping; + .addRefMapping(MetadataType.F_CREATE_APPROVER_REF, + QAssignmentReferenceMapping.initForAssignmentCreateApprover( + repositoryContext)) + .addRefMapping(MetadataType.F_MODIFY_APPROVER_REF, + QAssignmentReferenceMapping.initForAssignmentModifyApprover( + repositoryContext)); } @Override @@ -114,14 +156,11 @@ protected QAssignment newAliasInstance(String alias) { return new QAssignment<>(alias); } - @Override - public AssignmentSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new AssignmentSqlTransformer<>(transformerSupport, this); - } - @Override public MAssignment newRowObject() { - return new MAssignment(); + MAssignment row = new MAssignment(); + row.containerType = this.containerType; + return row; } @Override @@ -130,4 +169,84 @@ public MAssignment newRowObject(OR ownerRow) { row.ownerOid = ownerRow.oid; return row; } + + // about duplication see the comment in QObjectMapping.toRowObjectWithoutFullObject + @SuppressWarnings("DuplicatedCode") + @Override + public MAssignment insert(AssignmentType assignment, OR ownerRow, JdbcSession jdbcSession) { + MAssignment row = initRowObject(assignment, ownerRow); + + row.ownerType = ownerRow.objectType; + row.lifecycleState = assignment.getLifecycleState(); + row.orderValue = assignment.getOrder(); + setReference(assignment.getOrgRef(), + o -> row.orgRefTargetOid = o, + t -> row.orgRefTargetType = t, + r -> row.orgRefRelationId = r); + setReference(assignment.getTargetRef(), + o -> row.targetRefTargetOid = o, + t -> row.targetRefTargetType = t, + r -> row.targetRefRelationId = r); + setReference(assignment.getTenantRef(), + o -> row.tenantRefTargetOid = o, + t -> row.tenantRefTargetType = t, + r -> row.tenantRefRelationId = r); + + // TODO no idea how to do this yet, somehow related to RAssignment.extension +// row.extId = assignment.getExtension()...id?; +// row.extOid =; + row.policySituations = processCacheableUris(assignment.getPolicySituation()); + // TODO extensions stored inline (JSON) + + ConstructionType construction = assignment.getConstruction(); + if (construction != null) { + setReference(construction.getResourceRef(), + o -> row.resourceRefTargetOid = o, + t -> row.resourceRefTargetType = t, + r -> row.resourceRefRelationId = r); + } + + ActivationType activation = assignment.getActivation(); + if (activation != null) { + row.administrativeStatus = activation.getAdministrativeStatus(); + row.effectiveStatus = activation.getEffectiveStatus(); + row.enableTimestamp = MiscUtil.asInstant(activation.getEnableTimestamp()); + row.disableTimestamp = MiscUtil.asInstant(activation.getDisableTimestamp()); + row.disableReason = activation.getDisableReason(); + row.validityStatus = activation.getValidityStatus(); + row.validFrom = MiscUtil.asInstant(activation.getValidFrom()); + row.validTo = MiscUtil.asInstant(activation.getValidTo()); + row.validityChangeTimestamp = MiscUtil.asInstant(activation.getValidityChangeTimestamp()); + row.archiveTimestamp = MiscUtil.asInstant(activation.getArchiveTimestamp()); + } + + MetadataType metadata = assignment.getMetadata(); + if (metadata != null) { + setReference(metadata.getCreatorRef(), + o -> row.creatorRefTargetOid = o, + t -> row.creatorRefTargetType = t, + r -> row.creatorRefRelationId = r); + row.createChannelId = processCacheableUri(metadata.getCreateChannel()); + row.createTimestamp = MiscUtil.asInstant(metadata.getCreateTimestamp()); + + setReference(metadata.getModifierRef(), + o -> row.modifierRefTargetOid = o, + t -> row.modifierRefTargetType = t, + r -> row.modifierRefRelationId = r); + row.modifyChannelId = processCacheableUri(metadata.getModifyChannel()); + row.modifyTimestamp = MiscUtil.asInstant(metadata.getModifyTimestamp()); + } + + // insert before treating sub-entities + insert(row, jdbcSession); + + if (metadata != null) { + storeRefs(row, metadata.getCreateApproverRef(), + QAssignmentReferenceMapping.getForAssignmentCreateApprover(), jdbcSession); + storeRefs(row, metadata.getModifyApproverRef(), + QAssignmentReferenceMapping.getForAssignmentModifyApprover(), jdbcSession); + } + + return row; + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReferenceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReferenceMapping.java index 56fe38d3f24..a434fd16c0a 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReferenceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReferenceMapping.java @@ -6,31 +6,68 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.assignment; +import java.util.Objects; import java.util.function.BiFunction; import com.querydsl.core.types.Predicate; +import org.jetbrains.annotations.NotNull; +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReference; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReferenceMapping; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; /** - * Mapping between {@link QObjectReference} and {@link ObjectReferenceType}. - * The mapping is the same for all subtypes, see different `INSTANCE_*` constants below. + * Mapping between {@link QAssignmentReference} and {@link ObjectReferenceType}. + * The mapping is the same for all subtypes, see various static `get*()` methods below. + * Both mapping instances are initialized (`init*()` methods) in {@link QAssignmentMapping}. + * Init methods can be called multiple times, only one instance for each sub-tables is created. * * @param type of the row (M-bean) of the assignment owner */ public class QAssignmentReferenceMapping extends QReferenceMapping, MAssignment> { - public static final QAssignmentReferenceMapping INSTANCE_ASSIGNMENT_CREATE_APPROVER = - new QAssignmentReferenceMapping<>("m_assignment_ref_create_approver", "arefca"); - public static final QAssignmentReferenceMapping INSTANCE_ASSIGNMENT_MODIFY_APPROVER = - new QAssignmentReferenceMapping<>("m_assignment_ref_create_approver", "arefma"); + private static QAssignmentReferenceMapping instanceAssignmentCreateApprover; + private static QAssignmentReferenceMapping instanceAssignmentModifyApprover; - private QAssignmentReferenceMapping(String tableName, String defaultAliasName) { - super(tableName, defaultAliasName, QAssignmentReference.class); + public static QAssignmentReferenceMapping + initForAssignmentCreateApprover(@NotNull SqaleRepoContext repositoryContext) { + if (instanceAssignmentCreateApprover == null) { + instanceAssignmentCreateApprover = new QAssignmentReferenceMapping<>( + "m_assignment_ref_create_approver", "arefca", repositoryContext); + } + return getForAssignmentCreateApprover(); + } + + public static QAssignmentReferenceMapping + getForAssignmentCreateApprover() { + //noinspection unchecked + return (QAssignmentReferenceMapping) + Objects.requireNonNull(instanceAssignmentCreateApprover); + } + + public static QAssignmentReferenceMapping + initForAssignmentModifyApprover(@NotNull SqaleRepoContext repositoryContext) { + if (instanceAssignmentModifyApprover == null) { + instanceAssignmentModifyApprover = new QAssignmentReferenceMapping<>( + "m_assignment_ref_modify_approver", "arefma", repositoryContext); + } + return getForAssignmentModifyApprover(); + } + + public static QAssignmentReferenceMapping + getForAssignmentModifyApprover() { + //noinspection unchecked + return (QAssignmentReferenceMapping) + Objects.requireNonNull(instanceAssignmentModifyApprover); + } + + private QAssignmentReferenceMapping( + String tableName, + String defaultAliasName, + @NotNull SqaleRepoContext repositoryContext) { + super(tableName, defaultAliasName, QAssignmentReference.class, repositoryContext); // assignmentCid probably can't be mapped directly } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java index 92d6f162b04..9ee84f7589a 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java @@ -8,9 +8,10 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType.*; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; /** @@ -21,11 +22,13 @@ public class QCaseMapping public static final String DEFAULT_ALIAS_NAME = "cs"; - public static final QCaseMapping INSTANCE = new QCaseMapping(); + public static QCaseMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QCaseMapping(repositoryContext); + } - private QCaseMapping() { + private QCaseMapping(@NotNull SqaleRepoContext repositoryContext) { super(QCase.TABLE_NAME, DEFAULT_ALIAS_NAME, - CaseType.class, QCase.class); + CaseType.class, QCase.class, repositoryContext); addItemMapping(F_STATE, stringMapper(q -> q.state)); addItemMapping(F_CLOSE_TIMESTAMP, timestampMapper(q -> q.closeTimestamp)); @@ -52,15 +55,10 @@ protected QCase newAliasInstance(String alias) { return new QCase(alias); } - @Override - public ObjectSqlTransformer - createTransformer(SqlTransformerSupport transformerSupport) { - // no special class needed, no additional columns - return new ObjectSqlTransformer<>(transformerSupport, this); - } - @Override public MCase newRowObject() { return new MCase(); } + + // TODO transformation code } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainerMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainerMapping.java index 793303094a8..b78cd02cdc5 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainerMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainerMapping.java @@ -9,10 +9,10 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.prism.Containerable; +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.QOwnedByMapping; import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ContainerSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; /** * Mapping between {@link QContainer} and {@link Containerable}. @@ -28,17 +28,20 @@ public class QContainerMapping, MContainer, Object> INSTANCE = - new QContainerMapping<>(QContainer.TABLE_NAME, DEFAULT_ALIAS_NAME, - Containerable.class, QContainer.CLASS); + public static QContainerMapping initContainerMapping(@NotNull SqaleRepoContext repositoryContext) { + return new QContainerMapping<>( + QContainer.TABLE_NAME, DEFAULT_ALIAS_NAME, + Containerable.class, QContainer.CLASS, + repositoryContext); + } protected QContainerMapping( @NotNull String tableName, @NotNull String defaultAliasName, @NotNull Class schemaType, - @NotNull Class queryType) { - super(tableName, defaultAliasName, schemaType, queryType); + @NotNull Class queryType, + @NotNull SqaleRepoContext repositoryContext) { + super(tableName, defaultAliasName, schemaType, queryType, repositoryContext); // CID is not mapped directly, it is used by path resolver elsewhere } @@ -55,15 +58,24 @@ public R newRowObject(OR ownerRow) { "Container bean creation for owner row called on abstract container mapping"); } - @Override - public ContainerSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new ContainerSqlTransformer<>(transformerSupport, this); - } - @Override public R newRowObject() { //noinspection unchecked return (R) new MContainer(); } + + /** + * This creates the right type of object and fills in the base {@link MContainer} attributes. + */ + public R initRowObject(S schemaObject, OR ownerRow) { + R row = newRowObject(ownerRow); + row.cid = schemaObject.asPrismContainerValue().getId(); + // containerType is generated in DB, must be left null! + return row; + } + + @Override + public R insert(S schemaObject, OR ownerRow, JdbcSession jdbcSession) { + throw new UnsupportedOperationException("insert not implemented in the subclass"); + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/ConnectorHostSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/ConnectorHostSqlTransformer.java deleted file mode 100644 index 10479473c4e..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/ConnectorHostSqlTransformer.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.connector; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorHostType; - -public class ConnectorHostSqlTransformer - extends ObjectSqlTransformer { - - public ConnectorHostSqlTransformer( - SqlTransformerSupport transformerSupport, QConnectorHostMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MConnectorHost toRowObjectWithoutFullObject( - ConnectorHostType schemaObject, JdbcSession jdbcSession) { - MConnectorHost row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); - - row.hostname = schemaObject.getHostname(); - row.port = schemaObject.getPort(); - - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/ConnectorSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/ConnectorSqlTransformer.java deleted file mode 100644 index 8d36432ed34..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/ConnectorSqlTransformer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.connector; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorType; - -public class ConnectorSqlTransformer - extends ObjectSqlTransformer { - - public ConnectorSqlTransformer( - SqlTransformerSupport transformerSupport, QConnectorMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MConnector toRowObjectWithoutFullObject( - ConnectorType schemaObject, JdbcSession jdbcSession) { - MConnector row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); - - row.connectorBundle = schemaObject.getConnectorBundle(); - row.connectorType = schemaObject.getConnectorType(); - row.connectorVersion = schemaObject.getConnectorVersion(); - row.frameworkId = processCacheableUri(schemaObject.getFramework()); - - setReference(schemaObject.getConnectorHostRef(), - o -> row.connectorHostRefTargetOid = o, - t -> row.connectorHostRefTargetType = t, - r -> row.connectorHostRefRelationId = r); - - row.targetSystemTypes = arrayFor(schemaObject.getTargetSystemType()); - - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHostMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHostMapping.java index 2eb116abebf..ccf8474a3a6 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHostMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHostMapping.java @@ -9,8 +9,11 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorHostType.F_HOSTNAME; import static com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorHostType.F_PORT; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorHostType; /** @@ -21,11 +24,13 @@ public class QConnectorHostMapping public static final String DEFAULT_ALIAS_NAME = "conh"; - public static final QConnectorHostMapping INSTANCE = new QConnectorHostMapping(); + public static QConnectorHostMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QConnectorHostMapping(repositoryContext); + } - private QConnectorHostMapping() { + private QConnectorHostMapping(@NotNull SqaleRepoContext repositoryContext) { super(QConnectorHost.TABLE_NAME, DEFAULT_ALIAS_NAME, - ConnectorHostType.class, QConnectorHost.class); + ConnectorHostType.class, QConnectorHost.class, repositoryContext); addItemMapping(F_HOSTNAME, stringMapper(q -> q.hostname)); addItemMapping(F_PORT, stringMapper(q -> q.port)); @@ -37,12 +42,18 @@ protected QConnectorHost newAliasInstance(String alias) { } @Override - public ConnectorHostSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new ConnectorHostSqlTransformer(transformerSupport, this); + public MConnectorHost newRowObject() { + return new MConnectorHost(); } @Override - public MConnectorHost newRowObject() { - return new MConnectorHost(); + public @NotNull MConnectorHost toRowObjectWithoutFullObject( + ConnectorHostType schemaObject, JdbcSession jdbcSession) { + MConnectorHost row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); + + row.hostname = schemaObject.getHostname(); + row.port = schemaObject.getPort(); + + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java index 2b550bb8fc5..aebdedbbd45 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java @@ -8,8 +8,11 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorType.*; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorType; /** @@ -20,10 +23,13 @@ public class QConnectorMapping public static final String DEFAULT_ALIAS_NAME = "con"; - public static final QConnectorMapping INSTANCE = new QConnectorMapping(); + public static QConnectorMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QConnectorMapping(repositoryContext); + } - private QConnectorMapping() { - super(QConnector.TABLE_NAME, DEFAULT_ALIAS_NAME, ConnectorType.class, QConnector.class); + private QConnectorMapping(@NotNull SqaleRepoContext repositoryContext) { + super(QConnector.TABLE_NAME, DEFAULT_ALIAS_NAME, + ConnectorType.class, QConnector.class, repositoryContext); addItemMapping(F_CONNECTOR_BUNDLE, stringMapper(q -> q.connectorBundle)); addItemMapping(F_CONNECTOR_TYPE, stringMapper(q -> q.connectorType)); @@ -43,12 +49,27 @@ protected QConnector newAliasInstance(String alias) { } @Override - public ConnectorSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new ConnectorSqlTransformer(transformerSupport, this); + public MConnector newRowObject() { + return new MConnector(); } @Override - public MConnector newRowObject() { - return new MConnector(); + public @NotNull MConnector toRowObjectWithoutFullObject( + ConnectorType schemaObject, JdbcSession jdbcSession) { + MConnector row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); + + row.connectorBundle = schemaObject.getConnectorBundle(); + row.connectorType = schemaObject.getConnectorType(); + row.connectorVersion = schemaObject.getConnectorVersion(); + row.frameworkId = processCacheableUri(schemaObject.getFramework()); + + setReference(schemaObject.getConnectorHostRef(), + o -> row.connectorHostRefTargetOid = o, + t -> row.connectorHostRefTargetType = t, + r -> row.connectorHostRefRelationId = r); + + row.targetSystemTypes = arrayFor(schemaObject.getTargetSystemType()); + + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/FocusSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/FocusSqlTransformer.java deleted file mode 100644 index 266b2e10789..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/FocusSqlTransformer.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.focus; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; - -public class FocusSqlTransformer, R extends MFocus> - extends ObjectSqlTransformer { - - private final QFocusMapping mapping; - - public FocusSqlTransformer( - SqlTransformerSupport transformerSupport, QFocusMapping mapping) { - super(transformerSupport, mapping); - this.mapping = mapping; - } - - @SuppressWarnings("DuplicatedCode") // activation code duplicated with assignment - @Override - public @NotNull R toRowObjectWithoutFullObject(S focus, JdbcSession jdbcSession) { - R row = super.toRowObjectWithoutFullObject(focus, jdbcSession); - - row.costCenter = focus.getCostCenter(); - row.emailAddress = focus.getEmailAddress(); - row.photo = focus.getJpegPhoto(); - row.locale = focus.getLocale(); - setPolyString(focus.getLocality(), o -> row.localityOrig = o, n -> row.localityNorm = n); - row.preferredLanguage = focus.getPreferredLanguage(); - row.telephoneNumber = focus.getTelephoneNumber(); - row.timezone = focus.getTimezone(); - - // credential/password/metadata (sorry for nesting, but the gets may not be so cheap) - CredentialsType credentials = focus.getCredentials(); - if (credentials != null) { - PasswordType password = credentials.getPassword(); - if (password != null) { - MetadataType passwordMetadata = password.getMetadata(); - if (passwordMetadata != null) { - row.passwordCreateTimestamp = - MiscUtil.asInstant(passwordMetadata.getCreateTimestamp()); - row.passwordModifyTimestamp = - MiscUtil.asInstant(passwordMetadata.getModifyTimestamp()); - } - } - } - - // activation - ActivationType activation = focus.getActivation(); - if (activation != null) { - row.administrativeStatus = activation.getAdministrativeStatus(); - row.effectiveStatus = activation.getEffectiveStatus(); - row.enableTimestamp = MiscUtil.asInstant(activation.getEnableTimestamp()); - row.disableTimestamp = MiscUtil.asInstant(activation.getDisableTimestamp()); - row.disableReason = activation.getDisableReason(); - row.validityStatus = activation.getValidityStatus(); - row.validFrom = MiscUtil.asInstant(activation.getValidFrom()); - row.validTo = MiscUtil.asInstant(activation.getValidTo()); - row.validityChangeTimestamp = MiscUtil.asInstant(activation.getValidityChangeTimestamp()); - row.archiveTimestamp = MiscUtil.asInstant(activation.getArchiveTimestamp()); - row.lockoutStatus = activation.getLockoutStatus(); - } - - return row; - } - - @Override - public void storeRelatedEntities( - @NotNull R row, @NotNull S schemaObject, @NotNull JdbcSession jdbcSession) { - super.storeRelatedEntities(row, schemaObject, jdbcSession); - - storeRefs(row, schemaObject.getLinkRef(), - mapping.projectionReferenceMapping(), jdbcSession); - storeRefs(row, schemaObject.getPersonaRef(), - mapping.personaReferenceMapping(), jdbcSession); - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/GenericObjectSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/GenericObjectSqlTransformer.java deleted file mode 100644 index 8bfc9143b02..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/GenericObjectSqlTransformer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.focus; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.GenericObjectType; - -public class GenericObjectSqlTransformer - extends FocusSqlTransformer { - - public GenericObjectSqlTransformer( - SqlTransformerSupport transformerSupport, QGenericObjectMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MGenericObject toRowObjectWithoutFullObject( - GenericObjectType genericObject, JdbcSession jdbcSession) { - MGenericObject row = super.toRowObjectWithoutFullObject(genericObject, jdbcSession); - - row.genericObjectTypeId = processCacheableUri(genericObject.getObjectType()); - - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java index fd69cf7246b..92fb26d92ae 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java @@ -13,11 +13,13 @@ import com.querydsl.core.types.Path; import org.jetbrains.annotations.NotNull; +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; +import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; /** @@ -32,16 +34,19 @@ public class QFocusMapping, R extends M public static final String DEFAULT_ALIAS_NAME = "f"; - public static final QFocusMapping, MFocus> INSTANCE = - new QFocusMapping<>(QFocus.TABLE_NAME, DEFAULT_ALIAS_NAME, - FocusType.class, QFocus.CLASS); + public static QFocusMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QFocusMapping<>(QFocus.TABLE_NAME, DEFAULT_ALIAS_NAME, + FocusType.class, QFocus.CLASS, + repositoryContext); + } protected QFocusMapping( @NotNull String tableName, @NotNull String defaultAliasName, @NotNull Class schemaType, - @NotNull Class queryType) { - super(tableName, defaultAliasName, schemaType, queryType); + @NotNull Class queryType, + @NotNull SqaleRepoContext repositoryContext) { + super(tableName, defaultAliasName, schemaType, queryType, repositoryContext); addItemMapping(F_COST_CENTER, stringMapper(q -> q.costCenter)); addItemMapping(F_EMAIL_ADDRESS, stringMapper(q -> q.emailAddress)); @@ -84,20 +89,8 @@ protected QFocusMapping( .addItemMapping(ActivationType.F_LOCKOUT_STATUS, enumMapper(q -> q.lockoutStatus)); - addRefMapping(F_PERSONA_REF, personaReferenceMapping()); - addRefMapping(F_LINK_REF, projectionReferenceMapping()); - } - - /** Fixes rigid parametric types of static mapping instance to this instance. */ - public @NotNull QObjectReferenceMapping personaReferenceMapping() { - //noinspection unchecked - return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_PERSONA; - } - - /** Fixes rigid parametric types of static mapping instance to this instance. */ - public @NotNull QObjectReferenceMapping projectionReferenceMapping() { - //noinspection unchecked - return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_PROJECTION; + addRefMapping(F_PERSONA_REF, QObjectReferenceMapping.initForPersona(repositoryContext)); + addRefMapping(F_LINK_REF, QObjectReferenceMapping.initForProjection(repositoryContext)); } @Override @@ -113,15 +106,68 @@ protected Q newAliasInstance(String alias) { return (Q) new QFocus<>(MFocus.class, alias); } - @Override - public FocusSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new FocusSqlTransformer<>(transformerSupport, this); - } - @Override public R newRowObject() { //noinspection unchecked return (R) new MFocus(); } + + @SuppressWarnings("DuplicatedCode") // activation code duplicated with assignment + @Override + public @NotNull R toRowObjectWithoutFullObject(S focus, JdbcSession jdbcSession) { + R row = super.toRowObjectWithoutFullObject(focus, jdbcSession); + + row.costCenter = focus.getCostCenter(); + row.emailAddress = focus.getEmailAddress(); + row.photo = focus.getJpegPhoto(); + row.locale = focus.getLocale(); + setPolyString(focus.getLocality(), o -> row.localityOrig = o, n -> row.localityNorm = n); + row.preferredLanguage = focus.getPreferredLanguage(); + row.telephoneNumber = focus.getTelephoneNumber(); + row.timezone = focus.getTimezone(); + + // credential/password/metadata (sorry for nesting, but the gets may not be so cheap) + CredentialsType credentials = focus.getCredentials(); + if (credentials != null) { + PasswordType password = credentials.getPassword(); + if (password != null) { + MetadataType passwordMetadata = password.getMetadata(); + if (passwordMetadata != null) { + row.passwordCreateTimestamp = + MiscUtil.asInstant(passwordMetadata.getCreateTimestamp()); + row.passwordModifyTimestamp = + MiscUtil.asInstant(passwordMetadata.getModifyTimestamp()); + } + } + } + + // activation + ActivationType activation = focus.getActivation(); + if (activation != null) { + row.administrativeStatus = activation.getAdministrativeStatus(); + row.effectiveStatus = activation.getEffectiveStatus(); + row.enableTimestamp = MiscUtil.asInstant(activation.getEnableTimestamp()); + row.disableTimestamp = MiscUtil.asInstant(activation.getDisableTimestamp()); + row.disableReason = activation.getDisableReason(); + row.validityStatus = activation.getValidityStatus(); + row.validFrom = MiscUtil.asInstant(activation.getValidFrom()); + row.validTo = MiscUtil.asInstant(activation.getValidTo()); + row.validityChangeTimestamp = MiscUtil.asInstant(activation.getValidityChangeTimestamp()); + row.archiveTimestamp = MiscUtil.asInstant(activation.getArchiveTimestamp()); + row.lockoutStatus = activation.getLockoutStatus(); + } + + return row; + } + + @Override + public void storeRelatedEntities( + @NotNull R row, @NotNull S schemaObject, @NotNull JdbcSession jdbcSession) { + super.storeRelatedEntities(row, schemaObject, jdbcSession); + + storeRefs(row, schemaObject.getLinkRef(), + QObjectReferenceMapping.getForProjection(), jdbcSession); + storeRefs(row, schemaObject.getPersonaRef(), + QObjectReferenceMapping.getForPersona(), jdbcSession); + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QGenericObjectMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QGenericObjectMapping.java index 4b60efbb6f2..cbebd42c2a8 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QGenericObjectMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QGenericObjectMapping.java @@ -6,7 +6,10 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.focus; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.GenericObjectType; /** @@ -17,11 +20,13 @@ public class QGenericObjectMapping public static final String DEFAULT_ALIAS_NAME = "go"; - public static final QGenericObjectMapping INSTANCE = new QGenericObjectMapping(); + public static QGenericObjectMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QGenericObjectMapping(repositoryContext); + } - private QGenericObjectMapping() { + private QGenericObjectMapping(@NotNull SqaleRepoContext repositoryContext) { super(QGenericObject.TABLE_NAME, DEFAULT_ALIAS_NAME, - GenericObjectType.class, QGenericObject.class); + GenericObjectType.class, QGenericObject.class, repositoryContext); } @Override @@ -30,13 +35,17 @@ protected QGenericObject newAliasInstance(String alias) { } @Override - public GenericObjectSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new GenericObjectSqlTransformer(transformerSupport, this); + public MGenericObject newRowObject() { + return new MGenericObject(); } @Override - public MGenericObject newRowObject() { - return new MGenericObject(); + public @NotNull MGenericObject toRowObjectWithoutFullObject( + GenericObjectType genericObject, JdbcSession jdbcSession) { + MGenericObject row = super.toRowObjectWithoutFullObject(genericObject, jdbcSession); + + row.genericObjectTypeId = processCacheableUri(genericObject.getObjectType()); + + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QUserMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QUserMapping.java index bb47b491e48..e8941623253 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QUserMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QUserMapping.java @@ -8,7 +8,10 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.UserType.*; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; /** @@ -19,11 +22,13 @@ public class QUserMapping public static final String DEFAULT_ALIAS_NAME = "u"; - public static final QUserMapping INSTANCE = new QUserMapping(); + public static QUserMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QUserMapping(repositoryContext); + } - private QUserMapping() { + private QUserMapping(@NotNull SqaleRepoContext repositoryContext) { super(QUser.TABLE_NAME, DEFAULT_ALIAS_NAME, - UserType.class, QUser.class); + UserType.class, QUser.class, repositoryContext); addItemMapping(F_ADDITIONAL_NAME, polyStringMapper( q -> q.additionalNameOrig, q -> q.additionalNameNorm)); @@ -50,13 +55,33 @@ protected QUser newAliasInstance(String alias) { } @Override - public UserSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new UserSqlTransformer(transformerSupport, this); + public MUser newRowObject() { + return new MUser(); } @Override - public MUser newRowObject() { - return new MUser(); + public @NotNull MUser toRowObjectWithoutFullObject( + UserType user, JdbcSession jdbcSession) { + MUser row = super.toRowObjectWithoutFullObject(user, jdbcSession); + + setPolyString(user.getAdditionalName(), + o -> row.additionalNameOrig = o, n -> row.additionalNameNorm = n); + row.employeeNumber = user.getEmployeeNumber(); + setPolyString(user.getFamilyName(), + o -> row.familyNameOrig = o, n -> row.familyNameNorm = n); + setPolyString(user.getFullName(), o -> row.fullNameOrig = o, n -> row.fullNameNorm = n); + setPolyString(user.getGivenName(), o -> row.givenNameOrig = o, n -> row.givenNameNorm = n); + setPolyString(user.getHonorificPrefix(), + o -> row.honorificPrefixOrig = o, n -> row.honorificPrefixNorm = n); + setPolyString(user.getHonorificSuffix(), + o -> row.honorificSuffixOrig = o, n -> row.honorificSuffixNorm = n); + setPolyString(user.getNickName(), o -> row.nickNameOrig = o, n -> row.nickNameNorm = n); + setPolyString(user.getTitle(), o -> row.titleOrig = o, n -> row.titleNorm = n); + + // TODO: + // user.getOrganizationalUnit() -> m_user_organizational_unit + // user.getOrganization() -> m_user_organization + + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/UserSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/UserSqlTransformer.java deleted file mode 100644 index a2316192798..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/UserSqlTransformer.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.focus; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; - -public class UserSqlTransformer - extends FocusSqlTransformer { - - public UserSqlTransformer( - SqlTransformerSupport transformerSupport, QUserMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MUser toRowObjectWithoutFullObject( - UserType user, JdbcSession jdbcSession) { - MUser row = super.toRowObjectWithoutFullObject(user, jdbcSession); - - setPolyString(user.getAdditionalName(), - o -> row.additionalNameOrig = o, n -> row.additionalNameNorm = n); - row.employeeNumber = user.getEmployeeNumber(); - setPolyString(user.getFamilyName(), - o -> row.familyNameOrig = o, n -> row.familyNameNorm = n); - setPolyString(user.getFullName(), o -> row.fullNameOrig = o, n -> row.fullNameNorm = n); - setPolyString(user.getGivenName(), o -> row.givenNameOrig = o, n -> row.givenNameNorm = n); - setPolyString(user.getHonorificPrefix(), - o -> row.honorificPrefixOrig = o, n -> row.honorificPrefixNorm = n); - setPolyString(user.getHonorificSuffix(), - o -> row.honorificSuffixOrig = o, n -> row.honorificSuffixNorm = n); - setPolyString(user.getNickName(), o -> row.nickNameOrig = o, n -> row.nickNameNorm = n); - setPolyString(user.getTitle(), o -> row.titleOrig = o, n -> row.titleNorm = n); - - // TODO: - // user.getOrganizationalUnit() -> m_user_organizational_unit - // user.getOrganization() -> m_user_organization - - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/LookupTableRowSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/LookupTableRowSqlTransformer.java deleted file mode 100644 index b01d7baddd9..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/LookupTableRowSqlTransformer.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.lookuptable; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ContainerSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType; - -public class LookupTableRowSqlTransformer - extends ContainerSqlTransformer { - - public LookupTableRowSqlTransformer( - SqlTransformerSupport transformerSupport, QLookupTableRowMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public MLookupTableRow insert(LookupTableRowType lookupTableRow, - MLookupTable ownerRow, JdbcSession jdbcSession) { - - MLookupTableRow row = initRowObject(lookupTableRow, ownerRow); - row.key = lookupTableRow.getKey(); - row.value = lookupTableRow.getValue(); - setPolyString(lookupTableRow.getLabel(), o -> row.labelOrig = o, n -> row.labelNorm = n); - row.lastChangeTimestamp = MiscUtil.asInstant(lookupTableRow.getLastChangeTimestamp()); - - insert(row, jdbcSession); - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/LookupTableSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/LookupTableSqlTransformer.java deleted file mode 100644 index 21941971077..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/LookupTableSqlTransformer.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.lookuptable; - -import java.util.List; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; - -public class LookupTableSqlTransformer - extends ObjectSqlTransformer { - - public LookupTableSqlTransformer( - SqlTransformerSupport transformerSupport, QLookupTableMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public void storeRelatedEntities( - @NotNull MLookupTable lookupTable, - @NotNull LookupTableType schemaObject, - @NotNull JdbcSession jdbcSession) { - super.storeRelatedEntities(lookupTable, schemaObject, jdbcSession); - - List rows = schemaObject.getRow(); - if (!rows.isEmpty()) { - LookupTableRowSqlTransformer transformer = - QLookupTableRowMapping.INSTANCE.createTransformer(transformerSupport); - rows.forEach(row -> transformer.insert(row, lookupTable, jdbcSession)); - } - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableMapping.java index 405d6ef9877..a7723179d0f 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableMapping.java @@ -8,8 +8,14 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType.F_ROW; +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; /** @@ -20,13 +26,16 @@ public class QLookupTableMapping public static final String DEFAULT_ALIAS_NAME = "lt"; - public static final QLookupTableMapping INSTANCE = new QLookupTableMapping(); + public static QLookupTableMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QLookupTableMapping(repositoryContext); + } - private QLookupTableMapping() { + private QLookupTableMapping(@NotNull SqaleRepoContext repositoryContext) { super(QLookupTable.TABLE_NAME, DEFAULT_ALIAS_NAME, - LookupTableType.class, QLookupTable.class); + LookupTableType.class, QLookupTable.class, repositoryContext); - addContainerTableMapping(F_ROW, QLookupTableRowMapping.INSTANCE, + addContainerTableMapping(F_ROW, + QLookupTableRowMapping.init(repositoryContext), joinOn((o, t) -> o.oid.eq(t.ownerOid))); } @@ -36,12 +45,21 @@ protected QLookupTable newAliasInstance(String alias) { } @Override - public LookupTableSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new LookupTableSqlTransformer(transformerSupport, this); + public MLookupTable newRowObject() { + return new MLookupTable(); } @Override - public MLookupTable newRowObject() { - return new MLookupTable(); + public void storeRelatedEntities( + @NotNull MLookupTable lookupTable, + @NotNull LookupTableType schemaObject, + @NotNull JdbcSession jdbcSession) { + super.storeRelatedEntities(lookupTable, schemaObject, jdbcSession); + + List rows = schemaObject.getRow(); + if (!rows.isEmpty()) { + rows.forEach(row -> + QLookupTableRowMapping.get().insert(row, lookupTable, jdbcSession)); + } } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableRowMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableRowMapping.java index faaf1e9ae64..e7a295f5a91 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableRowMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableRowMapping.java @@ -8,8 +8,14 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType.*; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainerMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType; /** @@ -20,11 +26,22 @@ public class QLookupTableRowMapping public static final String DEFAULT_ALIAS_NAME = "ltr"; - public static final QLookupTableRowMapping INSTANCE = new QLookupTableRowMapping(); + private static QLookupTableRowMapping instance; + + public static QLookupTableRowMapping init(@NotNull SqaleRepoContext repositoryContext) { + if (instance == null) { + instance = new QLookupTableRowMapping(repositoryContext); + } + return instance; + } + + public static QLookupTableRowMapping get() { + return Objects.requireNonNull(instance); + } - private QLookupTableRowMapping() { + private QLookupTableRowMapping(@NotNull SqaleRepoContext repositoryContext) { super(QLookupTableRow.TABLE_NAME, DEFAULT_ALIAS_NAME, - LookupTableRowType.class, QLookupTableRow.class); + LookupTableRowType.class, QLookupTableRow.class, repositoryContext); addItemMapping(F_KEY, stringMapper(q -> q.key)); addItemMapping(F_LABEL, polyStringMapper( @@ -39,11 +56,6 @@ protected QLookupTableRow newAliasInstance(String alias) { return new QLookupTableRow(alias); } - @Override - public LookupTableRowSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new LookupTableRowSqlTransformer(transformerSupport, this); - } - @Override public MLookupTableRow newRowObject() { return new MLookupTableRow(); @@ -55,4 +67,18 @@ public MLookupTableRow newRowObject(MLookupTable ownerRow) { row.ownerOid = ownerRow.oid; return row; } + + @Override + public MLookupTableRow insert(LookupTableRowType lookupTableRow, + MLookupTable ownerRow, JdbcSession jdbcSession) { + + MLookupTableRow row = initRowObject(lookupTableRow, ownerRow); + row.key = lookupTableRow.getKey(); + row.value = lookupTableRow.getValue(); + setPolyString(lookupTableRow.getLabel(), o -> row.labelOrig = o, n -> row.labelNorm = n); + row.lastChangeTimestamp = MiscUtil.asInstant(lookupTableRow.getLastChangeTimestamp()); + + insert(row, jdbcSession); + return row; + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/NodeSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/NodeSqlTransformer.java deleted file mode 100644 index b6003939f26..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/NodeSqlTransformer.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.node; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.NodeType; - -public class NodeSqlTransformer - extends ObjectSqlTransformer { - - public NodeSqlTransformer( - SqlTransformerSupport transformerSupport, QNodeMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MNode toRowObjectWithoutFullObject(NodeType node, JdbcSession jdbcSession) { - MNode row = super.toRowObjectWithoutFullObject(node, jdbcSession); - - row.nodeIdentifier = node.getNodeIdentifier(); - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNodeMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNodeMapping.java index 38bf3917847..397cd366306 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNodeMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNodeMapping.java @@ -8,8 +8,11 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.NodeType.F_NODE_IDENTIFIER; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.NodeType; /** @@ -20,10 +23,13 @@ public class QNodeMapping public static final String DEFAULT_ALIAS_NAME = "nod"; - public static final QNodeMapping INSTANCE = new QNodeMapping(); + public static QNodeMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QNodeMapping(repositoryContext); + } - private QNodeMapping() { - super(QNode.TABLE_NAME, DEFAULT_ALIAS_NAME, NodeType.class, QNode.class); + private QNodeMapping(@NotNull SqaleRepoContext repositoryContext) { + super(QNode.TABLE_NAME, DEFAULT_ALIAS_NAME, + NodeType.class, QNode.class, repositoryContext); addItemMapping(F_NODE_IDENTIFIER, stringMapper(q -> q.nodeIdentifier)); } @@ -34,13 +40,15 @@ protected QNode newAliasInstance(String alias) { } @Override - public NodeSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new NodeSqlTransformer(transformerSupport, this); + public MNode newRowObject() { + return new MNode(); } @Override - public MNode newRowObject() { - return new MNode(); + public @NotNull MNode toRowObjectWithoutFullObject(NodeType node, JdbcSession jdbcSession) { + MNode row = super.toRowObjectWithoutFullObject(node, jdbcSession); + + row.nodeIdentifier = node.getNodeIdentifier(); + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ContainerSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ContainerSqlTransformer.java deleted file mode 100644 index 4788cdb3bef..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ContainerSqlTransformer.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.object; - -import com.evolveum.midpoint.prism.Containerable; -import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTransformerBase; -import com.evolveum.midpoint.repo.sqale.qmodel.TransformerForOwnedBy; -import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainer; -import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainer; -import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainerMapping; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; - -/** - * @param schema type - * @param type of entity path - * @param type of the row bean for the table - * @param type of the owner row (table owning the container) - */ -public class ContainerSqlTransformer - , R extends MContainer, OR> - extends SqaleTransformerBase - implements TransformerForOwnedBy { - - private final QContainerMapping mapping; - - public ContainerSqlTransformer( - SqlTransformerSupport transformerSupport, QContainerMapping mapping) { - super(transformerSupport); - this.mapping = mapping; - } - - @Override - protected QContainerMapping mapping() { - return mapping; - } - - /** - * This creates the right type of object and fills in the base {@link MContainer} attributes. - */ - public R initRowObject(S schemaObject, OR ownerRow) { - R row = mapping.newRowObject(ownerRow); - row.cid = schemaObject.asPrismContainerValue().getId(); - // containerType is generated in DB, must be left null! - return row; - } - - @Override - public R insert(S schemaObject, OR ownerRow, JdbcSession jdbcSession) { - throw new UnsupportedOperationException("insert not implemented in the subclass"); - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ObjectSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ObjectSqlTransformer.java deleted file mode 100644 index f34779d1dce..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/ObjectSqlTransformer.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.object; - -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import javax.xml.namespace.QName; - -import com.querydsl.core.Tuple; -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.SerializationOptions; -import com.evolveum.midpoint.repo.sqale.SqaleUtils; -import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTransformerBase; -import com.evolveum.midpoint.repo.sqale.qmodel.assignment.AssignmentSqlTransformer; -import com.evolveum.midpoint.repo.sqale.qmodel.common.QUri; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.SelectorOptions; -import com.evolveum.midpoint.schema.util.ObjectTypeUtil; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; - -public class ObjectSqlTransformer, R extends MObject> - extends SqaleTransformerBase { - - private final QObjectMapping mapping; - - public ObjectSqlTransformer( - SqlTransformerSupport transformerSupport, - QObjectMapping mapping) { - super(transformerSupport); - this.mapping = mapping; - } - - @Override - protected QObjectMapping mapping() { - return mapping; - } - - @Override - public S toSchemaObject(Tuple row, Q entityPath, - Collection> options) - throws SchemaException { - - byte[] fullObject = Objects.requireNonNull(row.get(entityPath.fullObject)); - - PrismObject prismObject; - String serializedForm = new String(fullObject, StandardCharsets.UTF_8); - try { - SqlTransformerSupport.ParseResult result = - transformerSupport.parsePrismObject(serializedForm); - prismObject = result.prismObject; - if (result.parsingContext.hasWarnings()) { - logger.warn("Object {} parsed with {} warnings", - ObjectTypeUtil.toShortString(prismObject), - result.parsingContext.getWarnings().size()); - } - } catch (SchemaException | RuntimeException | Error e) { - // This is a serious thing. We have corrupted XML in the repo. This may happen even - // during system init. We want really loud and detailed error here. - logger.error("Couldn't parse object {} {}: {}: {}\n{}", - mapping.schemaType().getSimpleName(), row.get(entityPath.oid), - e.getClass().getName(), e.getMessage(), serializedForm, e); - throw e; - } - - return prismObject.asObjectable(); - } - - /** - * Override this to fill additional row attributes after calling this super version. - * - * *This must be called with active JDBC session* so it can create new {@link QUri} rows. - * As this is intended for inserts *DO NOT* set {@link MObject#objectType} to any value, - * it must be NULL otherwise the DB will complain about the value for the generated column. - * - * OID may be null, hence the method does NOT create any sub-entities, see - * {@link #storeRelatedEntities(MObject, ObjectType, JdbcSession)}. - * Try to keep order of fields here, in M-class (MObject for this one) and in SQL the same. - */ - @SuppressWarnings("DuplicatedCode") // see comment for metadata lower - @NotNull - public R toRowObjectWithoutFullObject(S schemaObject, JdbcSession jdbcSession) { - R row = mapping.newRowObject(); - - row.oid = oidToUUid(schemaObject.getOid()); - // objectType MUST be left NULL for INSERT, it's determined by PG - setPolyString(schemaObject.getName(), o -> row.nameOrig = o, n -> row.nameNorm = n); - // fullObject is managed outside of this method - setReference(schemaObject.getTenantRef(), - o -> row.tenantRefTargetOid = o, - t -> row.tenantRefTargetType = t, - r -> row.tenantRefRelationId = r); - row.lifecycleState = schemaObject.getLifecycleState(); - // containerIdSeq is managed outside of this method - row.version = SqaleUtils.objectVersionAsInt(schemaObject); - - // complex DB fields - row.policySituations = processCacheableUris(schemaObject.getPolicySituation()); - row.subtypes = arrayFor(schemaObject.getSubtype()); - // TODO textInfo (fulltext support) - // repo.getTextInfoItems().addAll(RObjectTextInfo.createItemsSet(jaxb, repo, repositoryContext)); - // TODO extensions stored inline (JSON) - that is ext column - - // This is duplicate code with AssignmentSqlTransformer.toRowObject, but making interface - // and needed setters (fields are not "interface-able") would create much more code. - MetadataType metadata = schemaObject.getMetadata(); - if (metadata != null) { - setReference(metadata.getCreatorRef(), - o -> row.creatorRefTargetOid = o, - t -> row.creatorRefTargetType = t, - r -> row.creatorRefRelationId = r); - row.createChannelId = processCacheableUri(metadata.getCreateChannel()); - row.createTimestamp = MiscUtil.asInstant(metadata.getCreateTimestamp()); - - setReference(metadata.getModifierRef(), - o -> row.modifierRefTargetOid = o, - t -> row.modifierRefTargetType = t, - r -> row.modifierRefRelationId = r); - row.modifyChannelId = processCacheableUri(metadata.getModifyChannel()); - row.modifyTimestamp = MiscUtil.asInstant(metadata.getModifyTimestamp()); - } - return row; - } - - /** - * Stores other entities related to the main object row like containers, references, etc. - * This is not part of {@link #toRowObjectWithoutFullObject} because it requires know OID - * which is not assured before calling that method. - * - * *Always call this super method first in overriding methods.* - * - * @param row master row for the added object("aggregate root") - * @param schemaObject schema objects for which the details are stored - * @param jdbcSession JDBC session used to insert related rows - */ - public void storeRelatedEntities( - @NotNull R row, @NotNull S schemaObject, @NotNull JdbcSession jdbcSession) { - Objects.requireNonNull(row.oid); - - // We're after insert, we can set this for the needs of owned entities (assignments). - row.objectType = MObjectType.fromSchemaType(schemaObject.getClass()); - - MetadataType metadata = schemaObject.getMetadata(); - if (metadata != null) { - storeRefs(row, metadata.getCreateApproverRef(), - mapping.objectCreateApproverReferenceMapping(), jdbcSession); - storeRefs(row, metadata.getModifyApproverRef(), - mapping.objectModifyApproverReferenceMapping(), jdbcSession); - } - - List triggers = schemaObject.getTrigger(); - if (!triggers.isEmpty()) { - TriggerSqlTransformer transformer = - mapping.triggerMapping().createTransformer(transformerSupport); - triggers.forEach(t -> transformer.insert(t, row, jdbcSession)); - } - - List operationExecutions = schemaObject.getOperationExecution(); - if (!operationExecutions.isEmpty()) { - OperationExecutionSqlTransformer transformer = - mapping.operationExecutionMapping().createTransformer(transformerSupport); - operationExecutions.forEach(oe -> transformer.insert(oe, row, jdbcSession)); - } - - storeRefs(row, schemaObject.getParentOrgRef(), - mapping.objectParentOrgReferenceMapping(), jdbcSession); - - if (schemaObject instanceof AssignmentHolderType) { - storeAssignmentHolderEntities(row, (AssignmentHolderType) schemaObject, jdbcSession); - } - - /* TODO EAV extensions - the relevant code from old repo RObject#copyObjectInformationFromJAXB - if (jaxb.getExtension() != null) { - copyExtensionOrAttributesFromJAXB(jaxb.getExtension().asPrismContainerValue(), repo, repositoryContext, RObjectExtensionType.EXTENSION, generatorResult); - } - */ - } - - private void storeAssignmentHolderEntities( - R row, AssignmentHolderType schemaObject, JdbcSession jdbcSession) { - List assignments = schemaObject.getAssignment(); - if (!assignments.isEmpty()) { - AssignmentSqlTransformer transformer = - mapping.assignmentMapping().createTransformer(transformerSupport); - assignments.forEach(assignment -> - transformer.insert(assignment, row, jdbcSession)); - } - - storeRefs(row, schemaObject.getArchetypeRef(), - mapping.archetypeReferenceMapping(), jdbcSession); - storeRefs(row, schemaObject.getDelegatedRef(), - mapping.delegatedReferenceMapping(), jdbcSession); - storeRefs(row, schemaObject.getRoleMembershipRef(), - mapping.roleMembershipReferenceMapping(), jdbcSession); - } - - /** - * Serializes schema object and sets {@link R#fullObject}. - */ - public void setFullObject(R row, S schemaObject) throws SchemaException { - row.fullObject = createFullObject(schemaObject); - } - - public byte[] createFullObject(S schemaObject) throws SchemaException { - if (schemaObject.getOid() == null || schemaObject.getVersion() == null) { - throw new IllegalArgumentException( - "Serialized object must have assigned OID and version: " + schemaObject); - } - - return transformerSupport.createStringSerializer() - .itemsToSkip(fullObjectItemsToSkip()) - .options(SerializationOptions - .createSerializeReferenceNamesForNullOids() - .skipIndexOnly(true) - .skipTransient(true)) - .serialize(schemaObject.asPrismObject()) - .getBytes(StandardCharsets.UTF_8); - } - - protected Collection fullObjectItemsToSkip() { - // TODO extend later, things like FocusType.F_JPEG_PHOTO, see ObjectUpdater#updateFullObject - return Collections.emptyList(); - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/OperationExecutionSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/OperationExecutionSqlTransformer.java deleted file mode 100644 index 3048955b408..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/OperationExecutionSqlTransformer.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.object; - -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationExecutionType; - -public class OperationExecutionSqlTransformer - extends ContainerSqlTransformer, MOperationExecution, OR> { - - public OperationExecutionSqlTransformer( - SqlTransformerSupport transformerSupport, QOperationExecutionMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public MOperationExecution insert( - OperationExecutionType schemaObject, OR ownerRow, JdbcSession jdbcSession) { - MOperationExecution row = initRowObject(schemaObject, ownerRow); - - row.status = schemaObject.getStatus(); - row.recordType = schemaObject.getRecordType(); - setReference(schemaObject.getInitiatorRef(), - o -> row.initiatorRefTargetOid = o, - t -> row.initiatorRefTargetType = t, - r -> row.initiatorRefRelationId = r); - setReference(schemaObject.getTaskRef(), - o -> row.taskRefTargetOid = o, - t -> row.taskRefTargetType = t, - r -> row.taskRefRelationId = r); - row.timestampValue = MiscUtil.asInstant(schemaObject.getTimestamp()); - - insert(row, jdbcSession); - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolderMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolderMapping.java index b4afee53417..170ce467d08 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolderMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolderMapping.java @@ -6,6 +6,9 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.object; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType; /** @@ -21,11 +24,14 @@ public class QAssignmentHolderMapping extends QObjectMapping< public static final String DEFAULT_ALIAS_NAME = "ah"; - public static final QAssignmentHolderMapping INSTANCE = new QAssignmentHolderMapping(); + public static QAssignmentHolderMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QAssignmentHolderMapping(repositoryContext); + } - protected QAssignmentHolderMapping() { + protected QAssignmentHolderMapping(@NotNull SqaleRepoContext repositoryContext) { super(QObject.TABLE_NAME, DEFAULT_ALIAS_NAME, - AssignmentHolderType.class, QAssignmentHolder.class); + AssignmentHolderType.class, QAssignmentHolder.class, + repositoryContext); } public static class MAssignmentHolder extends MObject { diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java index f33c8922acb..1f547918431 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java @@ -8,22 +8,34 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType.*; +import java.nio.charset.StandardCharsets; import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import javax.xml.namespace.QName; +import com.querydsl.core.Tuple; import com.querydsl.core.types.Path; import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.prism.PrismConstants; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.SerializationOptions; +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; +import com.evolveum.midpoint.repo.sqale.SqaleUtils; import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping; import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.common.QUri; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.repo.sqlbase.mapping.SqlTransformer; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.repo.sqlbase.RepositoryObjectParseResult; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.util.MiscUtil; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; /** * Mapping between {@link QObject} and {@link ObjectType}. @@ -37,16 +49,20 @@ public class QObjectMapping, R extend public static final String DEFAULT_ALIAS_NAME = "o"; - public static final QObjectMapping, MObject> INSTANCE = - new QObjectMapping<>(QObject.TABLE_NAME, DEFAULT_ALIAS_NAME, - ObjectType.class, QObject.CLASS); + public static QObjectMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QObjectMapping<>( + QObject.TABLE_NAME, DEFAULT_ALIAS_NAME, + ObjectType.class, QObject.CLASS, + repositoryContext); + } protected QObjectMapping( @NotNull String tableName, @NotNull String defaultAliasName, @NotNull Class schemaType, - @NotNull Class queryType) { - super(tableName, defaultAliasName, schemaType, queryType); + @NotNull Class queryType, + @NotNull SqaleRepoContext repositoryContext) { + super(tableName, defaultAliasName, schemaType, queryType, repositoryContext); addItemMapping(PrismConstants.T_ID, uuidMapper(q -> q.oid)); addItemMapping(F_NAME, polyStringMapper( @@ -79,104 +95,228 @@ protected QObjectMapping( .addItemMapping(MetadataType.F_MODIFY_TIMESTAMP, timestampMapper(q -> q.modifyTimestamp)) .addRefMapping(MetadataType.F_CREATE_APPROVER_REF, - objectCreateApproverReferenceMapping()) + QObjectReferenceMapping.initForObjectCreateApprover(repositoryContext)) .addRefMapping(MetadataType.F_MODIFY_APPROVER_REF, - objectModifyApproverReferenceMapping()); + QObjectReferenceMapping.initForObjectModifyApprover(repositoryContext)); - addRefMapping(F_PARENT_ORG_REF, objectParentOrgReferenceMapping()); + addRefMapping(F_PARENT_ORG_REF, + QObjectReferenceMapping.initForObjectParentOrg(repositoryContext)); - addContainerTableMapping(AssignmentHolderType.F_ASSIGNMENT, assignmentMapping(), + addContainerTableMapping(AssignmentHolderType.F_ASSIGNMENT, + QAssignmentMapping.initAssignment(repositoryContext), joinOn((o, a) -> o.oid.eq(a.ownerOid))); - addContainerTableMapping(F_OPERATION_EXECUTION, operationExecutionMapping(), + addContainerTableMapping(F_OPERATION_EXECUTION, + QOperationExecutionMapping.init(repositoryContext), joinOn((o, trg) -> o.oid.eq(trg.ownerOid))); - addContainerTableMapping(F_TRIGGER, triggerMapping(), + addContainerTableMapping(F_TRIGGER, + QTriggerMapping.init(repositoryContext), joinOn((o, trg) -> o.oid.eq(trg.ownerOid))); // AssignmentHolderType - addRefMapping(F_ARCHETYPE_REF, archetypeReferenceMapping()); - addRefMapping(F_DELEGATED_REF, delegatedReferenceMapping()); - addRefMapping(F_ROLE_MEMBERSHIP_REF, roleMembershipReferenceMapping()); + addRefMapping(F_ARCHETYPE_REF, QObjectReferenceMapping.initForArchetype(repositoryContext)); + addRefMapping(F_DELEGATED_REF, QObjectReferenceMapping.initForDelegated(repositoryContext)); + addRefMapping(F_ROLE_MEMBERSHIP_REF, + QObjectReferenceMapping.initForRoleMembership(repositoryContext)); } - /** Fixes rigid parametric types of static mapping instance to this instance. */ - @NotNull - public QAssignmentMapping assignmentMapping() { - //noinspection unchecked - return (QAssignmentMapping) QAssignmentMapping.INSTANCE; + @Override + public @NotNull Path[] selectExpressions( + Q entity, Collection> options) { + return new Path[] { entity.oid, entity.fullObject }; } - /** Fixes rigid parametric types of static mapping instance to this instance. */ - @NotNull - public QOperationExecutionMapping operationExecutionMapping() { + @Override + protected Q newAliasInstance(String alias) { //noinspection unchecked - return (QOperationExecutionMapping) QOperationExecutionMapping.INSTANCE; + return (Q) new QObject<>(MObject.class, alias); } - /** Fixes rigid parametric types of static mapping instance to this instance. */ - @NotNull - public QTriggerMapping triggerMapping() { + @Override + public R newRowObject() { //noinspection unchecked - return (QTriggerMapping) QTriggerMapping.INSTANCE; + return (R) new MObject(); } - /** Fixes rigid parametric types of static mapping instance to this instance. */ - public @NotNull QObjectReferenceMapping archetypeReferenceMapping() { - //noinspection unchecked - return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_ARCHETYPE; - } + // region transformation + @Override + public S toSchemaObject(Tuple row, Q entityPath, + Collection> options) + throws SchemaException { - /** Fixes rigid parametric types of static mapping instance to this instance. */ - public @NotNull QObjectReferenceMapping delegatedReferenceMapping() { - //noinspection unchecked - return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_DELEGATED; - } + byte[] fullObject = Objects.requireNonNull(row.get(entityPath.fullObject)); - /** Fixes rigid parametric types of static mapping instance to this instance. */ - public @NotNull QObjectReferenceMapping objectCreateApproverReferenceMapping() { - //noinspection unchecked - return (QObjectReferenceMapping) - QObjectReferenceMapping.INSTANCE_OBJECT_CREATE_APPROVER; - } + PrismObject prismObject; + String serializedForm = new String(fullObject, StandardCharsets.UTF_8); + try { + RepositoryObjectParseResult result = + repositoryContext().parsePrismObject(serializedForm); + prismObject = result.prismObject; + if (result.parsingContext.hasWarnings()) { + logger.warn("Object {} parsed with {} warnings", + ObjectTypeUtil.toShortString(prismObject), + result.parsingContext.getWarnings().size()); + } + } catch (SchemaException | RuntimeException | Error e) { + // This is a serious thing. We have corrupted XML in the repo. This may happen even + // during system init. We want really loud and detailed error here. + logger.error("Couldn't parse object {} {}: {}: {}\n{}", + schemaType().getSimpleName(), row.get(entityPath.oid), + e.getClass().getName(), e.getMessage(), serializedForm, e); + throw e; + } - /** Fixes rigid parametric types of static mapping instance to this instance. */ - public @NotNull QObjectReferenceMapping objectModifyApproverReferenceMapping() { - //noinspection unchecked - return (QObjectReferenceMapping) - QObjectReferenceMapping.INSTANCE_OBJECT_MODIFY_APPROVER; + return prismObject.asObjectable(); } - /** Fixes rigid parametric types of static mapping instance to this instance. */ - public @NotNull QObjectReferenceMapping objectParentOrgReferenceMapping() { - //noinspection unchecked - return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_OBJECT_PARENT_ORG; + /** + * Override this to fill additional row attributes after calling this super version. + * + * *This must be called with active JDBC session* so it can create new {@link QUri} rows. + * As this is intended for inserts *DO NOT* set {@link MObject#objectType} to any value, + * it must be NULL otherwise the DB will complain about the value for the generated column. + * + * OID may be null, hence the method does NOT create any sub-entities, see + * {@link #storeRelatedEntities(MObject, ObjectType, JdbcSession)}. + * Try to keep order of fields here, in M-class (MObject for this one) and in SQL the same. + */ + @SuppressWarnings("DuplicatedCode") // see comment for metadata lower + @NotNull + public R toRowObjectWithoutFullObject(S schemaObject, JdbcSession jdbcSession) { + R row = newRowObject(); + + row.oid = oidToUUid(schemaObject.getOid()); + // objectType MUST be left NULL for INSERT, it's determined by PG + setPolyString(schemaObject.getName(), o -> row.nameOrig = o, n -> row.nameNorm = n); + // fullObject is managed outside of this method + setReference(schemaObject.getTenantRef(), + o -> row.tenantRefTargetOid = o, + t -> row.tenantRefTargetType = t, + r -> row.tenantRefRelationId = r); + row.lifecycleState = schemaObject.getLifecycleState(); + // containerIdSeq is managed outside of this method + row.version = SqaleUtils.objectVersionAsInt(schemaObject); + + // complex DB fields + row.policySituations = processCacheableUris(schemaObject.getPolicySituation()); + row.subtypes = arrayFor(schemaObject.getSubtype()); + // TODO textInfo (fulltext support) + // repo.getTextInfoItems().addAll(RObjectTextInfo.createItemsSet(jaxb, repo, repositoryContext)); + // TODO extensions stored inline (JSON) - that is ext column + + // This is duplicate code with QAssignmentMapping.insert, but making interface + // and needed setters (fields are not "interface-able") would create much more code. + MetadataType metadata = schemaObject.getMetadata(); + if (metadata != null) { + setReference(metadata.getCreatorRef(), + o -> row.creatorRefTargetOid = o, + t -> row.creatorRefTargetType = t, + r -> row.creatorRefRelationId = r); + row.createChannelId = processCacheableUri(metadata.getCreateChannel()); + row.createTimestamp = MiscUtil.asInstant(metadata.getCreateTimestamp()); + + setReference(metadata.getModifierRef(), + o -> row.modifierRefTargetOid = o, + t -> row.modifierRefTargetType = t, + r -> row.modifierRefRelationId = r); + row.modifyChannelId = processCacheableUri(metadata.getModifyChannel()); + row.modifyTimestamp = MiscUtil.asInstant(metadata.getModifyTimestamp()); + } + return row; } - /** Fixes rigid parametric types of static mapping instance to this instance. */ - public @NotNull QObjectReferenceMapping roleMembershipReferenceMapping() { - //noinspection unchecked - return (QObjectReferenceMapping) QObjectReferenceMapping.INSTANCE_ROLE_MEMBERSHIP; + /** + * Stores other entities related to the main object row like containers, references, etc. + * This is not part of {@link #toRowObjectWithoutFullObject} because it requires know OID + * which is not assured before calling that method. + * + * *Always call this super method first in overriding methods.* + * + * @param row master row for the added object("aggregate root") + * @param schemaObject schema objects for which the details are stored + * @param jdbcSession JDBC session used to insert related rows + */ + public void storeRelatedEntities( + @NotNull R row, @NotNull S schemaObject, @NotNull JdbcSession jdbcSession) { + Objects.requireNonNull(row.oid); + + // We're after insert, we can set this for the needs of owned entities (assignments). + row.objectType = MObjectType.fromSchemaType(schemaObject.getClass()); + + MetadataType metadata = schemaObject.getMetadata(); + if (metadata != null) { + storeRefs(row, metadata.getCreateApproverRef(), + QObjectReferenceMapping.getForObjectCreateApprover(), jdbcSession); + storeRefs(row, metadata.getModifyApproverRef(), + QObjectReferenceMapping.getForObjectModifyApprover(), jdbcSession); + } + + List triggers = schemaObject.getTrigger(); + if (!triggers.isEmpty()) { + triggers.forEach(t -> QTriggerMapping.get().insert(t, row, jdbcSession)); + } + + List operationExecutions = schemaObject.getOperationExecution(); + if (!operationExecutions.isEmpty()) { + operationExecutions.forEach(oe -> + QOperationExecutionMapping.get().insert(oe, row, jdbcSession)); + } + + storeRefs(row, schemaObject.getParentOrgRef(), + QObjectReferenceMapping.getForObjectParentOrg(), jdbcSession); + + if (schemaObject instanceof AssignmentHolderType) { + storeAssignmentHolderEntities(row, (AssignmentHolderType) schemaObject, jdbcSession); + } + + /* TODO EAV extensions - the relevant code from old repo RObject#copyObjectInformationFromJAXB + if (jaxb.getExtension() != null) { + copyExtensionOrAttributesFromJAXB(jaxb.getExtension().asPrismContainerValue(), repo, repositoryContext, RObjectExtensionType.EXTENSION, generatorResult); + } + */ } - @Override - public @NotNull Path[] selectExpressions( - Q entity, Collection> options) { - return new Path[] { entity.oid, entity.fullObject }; + private void storeAssignmentHolderEntities( + R row, AssignmentHolderType schemaObject, JdbcSession jdbcSession) { + List assignments = schemaObject.getAssignment(); + if (!assignments.isEmpty()) { + assignments.forEach(assignment -> + QAssignmentMapping.getAssignment().insert(assignment, row, jdbcSession)); + } + + storeRefs(row, schemaObject.getArchetypeRef(), + QObjectReferenceMapping.getForArchetype(), jdbcSession); + storeRefs(row, schemaObject.getDelegatedRef(), + QObjectReferenceMapping.getForDelegated(), jdbcSession); + storeRefs(row, schemaObject.getRoleMembershipRef(), + QObjectReferenceMapping.getForRoleMembership(), jdbcSession); } - @Override - protected Q newAliasInstance(String alias) { - //noinspection unchecked - return (Q) new QObject<>(MObject.class, alias); + /** + * Serializes schema object and sets {@link R#fullObject}. + */ + public void setFullObject(R row, S schemaObject) throws SchemaException { + row.fullObject = createFullObject(schemaObject); } - @Override - public SqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new ObjectSqlTransformer<>(transformerSupport, this); + public byte[] createFullObject(S schemaObject) throws SchemaException { + if (schemaObject.getOid() == null || schemaObject.getVersion() == null) { + throw new IllegalArgumentException( + "Serialized object must have assigned OID and version: " + schemaObject); + } + + return repositoryContext().createStringSerializer() + .itemsToSkip(fullObjectItemsToSkip()) + .options(SerializationOptions + .createSerializeReferenceNamesForNullOids() + .skipIndexOnly(true) + .skipTransient(true)) + .serialize(schemaObject.asPrismObject()) + .getBytes(StandardCharsets.UTF_8); } - @Override - public R newRowObject() { - //noinspection unchecked - return (R) new MObject(); + protected Collection fullObjectItemsToSkip() { + // TODO extend later, things like FocusType.F_JPEG_PHOTO, see ObjectUpdater#updateFullObject + return Collections.emptyList(); } + // endregion } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QOperationExecutionMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QOperationExecutionMapping.java index 427bed89379..8e1fd6c039a 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QOperationExecutionMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QOperationExecutionMapping.java @@ -8,8 +8,14 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.OperationExecutionType.*; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainerMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationExecutionType; /** @@ -22,14 +28,26 @@ public class QOperationExecutionMapping public static final String DEFAULT_ALIAS_NAME = "opex"; - public static final QOperationExecutionMapping INSTANCE = - new QOperationExecutionMapping<>(); + private static QOperationExecutionMapping instance; + + public static QOperationExecutionMapping init( + @NotNull SqaleRepoContext repositoryContext) { + if (instance == null) { + instance = new QOperationExecutionMapping<>(repositoryContext); + } + return get(); + } + + public static QOperationExecutionMapping get() { + //noinspection unchecked + return (QOperationExecutionMapping) Objects.requireNonNull(instance); + } // We can't declare Class>.class, so we cheat a bit. @SuppressWarnings({ "unchecked", "rawtypes" }) - private QOperationExecutionMapping() { + private QOperationExecutionMapping(@NotNull SqaleRepoContext repositoryContext) { super(QOperationExecution.TABLE_NAME, DEFAULT_ALIAS_NAME, - OperationExecutionType.class, (Class) QOperationExecution.class); + OperationExecutionType.class, (Class) QOperationExecution.class, repositoryContext); addItemMapping(F_STATUS, enumMapper(q -> q.status)); addItemMapping(F_RECORD_TYPE, enumMapper(q -> q.recordType)); @@ -50,12 +68,6 @@ protected QOperationExecution newAliasInstance(String alias) { return new QOperationExecution<>(alias); } - @Override - public OperationExecutionSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new OperationExecutionSqlTransformer<>(transformerSupport, this); - } - @Override public MOperationExecution newRowObject() { return new MOperationExecution(); @@ -67,4 +79,25 @@ public MOperationExecution newRowObject(OR ownerRow) { row.ownerOid = ownerRow.oid; return row; } + + @Override + public MOperationExecution insert( + OperationExecutionType schemaObject, OR ownerRow, JdbcSession jdbcSession) { + MOperationExecution row = initRowObject(schemaObject, ownerRow); + + row.status = schemaObject.getStatus(); + row.recordType = schemaObject.getRecordType(); + setReference(schemaObject.getInitiatorRef(), + o -> row.initiatorRefTargetOid = o, + t -> row.initiatorRefTargetType = t, + r -> row.initiatorRefRelationId = r); + setReference(schemaObject.getTaskRef(), + o -> row.taskRefTargetOid = o, + t -> row.taskRefTargetType = t, + r -> row.taskRefRelationId = r); + row.timestampValue = MiscUtil.asInstant(schemaObject.getTimestamp()); + + insert(row, jdbcSession); + return row; + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QTriggerMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QTriggerMapping.java index dd1019ddf14..49e0495fb21 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QTriggerMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QTriggerMapping.java @@ -6,8 +6,14 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.object; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainerMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; /** @@ -20,13 +26,26 @@ public class QTriggerMapping public static final String DEFAULT_ALIAS_NAME = "trg"; - public static final QTriggerMapping INSTANCE = new QTriggerMapping<>(); + private static QTriggerMapping instance; + + public static QTriggerMapping init( + @NotNull SqaleRepoContext repositoryContext) { + if (instance == null) { + instance = new QTriggerMapping<>(repositoryContext); + } + return get(); + } + + public static QTriggerMapping get() { + //noinspection unchecked + return (QTriggerMapping) Objects.requireNonNull(instance); + } // We can't declare Class>.class, so we cheat a bit. @SuppressWarnings({ "unchecked", "rawtypes" }) - private QTriggerMapping() { + private QTriggerMapping(@NotNull SqaleRepoContext repositoryContext) { super(QTrigger.TABLE_NAME, DEFAULT_ALIAS_NAME, - TriggerType.class, (Class) QTrigger.class); + TriggerType.class, (Class) QTrigger.class, repositoryContext); addItemMapping(TriggerType.F_HANDLER_URI, uriMapper(q -> q.handlerUriId)); addItemMapping(TriggerType.F_TIMESTAMP, timestampMapper(q -> q.timestampValue)); @@ -37,11 +56,6 @@ protected QTrigger newAliasInstance(String alias) { return new QTrigger<>(alias); } - @Override - public TriggerSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new TriggerSqlTransformer<>(transformerSupport, this); - } - @Override public MTrigger newRowObject() { return new MTrigger(); @@ -53,4 +67,15 @@ public MTrigger newRowObject(OR ownerRow) { row.ownerOid = ownerRow.oid; return row; } + + @Override + public MTrigger insert(TriggerType schemaObject, OR ownerRow, JdbcSession jdbcSession) { + MTrigger row = initRowObject(schemaObject, ownerRow); + + row.handlerUriId = processCacheableUri(schemaObject.getHandlerUri()); + row.timestampValue = MiscUtil.asInstant(schemaObject.getTimestamp()); + + insert(row, jdbcSession); + return row; + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/TriggerSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/TriggerSqlTransformer.java deleted file mode 100644 index 77c42cb0974..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/TriggerSqlTransformer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.object; - -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; - -public class TriggerSqlTransformer - extends ContainerSqlTransformer, MTrigger, OR> { - - public TriggerSqlTransformer( - SqlTransformerSupport transformerSupport, QTriggerMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public MTrigger insert(TriggerType schemaObject, OR ownerRow, JdbcSession jdbcSession) { - MTrigger row = initRowObject(schemaObject, ownerRow); - - row.handlerUriId = processCacheableUri(schemaObject.getHandlerUri()); - row.timestampValue = MiscUtil.asInstant(schemaObject.getTimestamp()); - - insert(row, jdbcSession); - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboardMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboardMapping.java index 79a2ce5f5f4..6b53c75d151 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboardMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboardMapping.java @@ -6,10 +6,11 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.other; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.DashboardType; /** @@ -20,11 +21,13 @@ public class QDashboardMapping public static final String DEFAULT_ALIAS_NAME = "d"; - public static final QDashboardMapping INSTANCE = new QDashboardMapping(); + public static QDashboardMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QDashboardMapping(repositoryContext); + } - private QDashboardMapping() { + private QDashboardMapping(@NotNull SqaleRepoContext repositoryContext) { super(QDashboard.TABLE_NAME, DEFAULT_ALIAS_NAME, - DashboardType.class, QDashboard.class); + DashboardType.class, QDashboard.class, repositoryContext); } @Override @@ -32,13 +35,6 @@ protected QDashboard newAliasInstance(String alias) { return new QDashboard(alias); } - @Override - public ObjectSqlTransformer - createTransformer(SqlTransformerSupport transformerSupport) { - // no special class needed, no additional columns - return new ObjectSqlTransformer<>(transformerSupport, this); - } - @Override public MObject newRowObject() { return new MObject(); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFormMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFormMapping.java index cb52224d9ef..d4d3c5fab43 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFormMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFormMapping.java @@ -6,10 +6,11 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.other; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.FormType; /** @@ -19,11 +20,13 @@ public class QFormMapping extends QObjectMapping { public static final String DEFAULT_ALIAS_NAME = "form"; - public static final QFormMapping INSTANCE = new QFormMapping(); + public static QFormMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QFormMapping(repositoryContext); + } - private QFormMapping() { + private QFormMapping(@NotNull SqaleRepoContext repositoryContext) { super(QForm.TABLE_NAME, DEFAULT_ALIAS_NAME, - FormType.class, QForm.class); + FormType.class, QForm.class, repositoryContext); } @Override @@ -31,13 +34,6 @@ protected QForm newAliasInstance(String alias) { return new QForm(alias); } - @Override - public ObjectSqlTransformer - createTransformer(SqlTransformerSupport transformerSupport) { - // no special class needed, no additional columns - return new ObjectSqlTransformer<>(transformerSupport, this); - } - @Override public MObject newRowObject() { return new MObject(); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibraryMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibraryMapping.java index 4045a9c7354..c9840342900 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibraryMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibraryMapping.java @@ -6,10 +6,11 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.other; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.FunctionLibraryType; /** @@ -20,11 +21,13 @@ public class QFunctionLibraryMapping public static final String DEFAULT_ALIAS_NAME = "flib"; - public static final QFunctionLibraryMapping INSTANCE = new QFunctionLibraryMapping(); + public static QFunctionLibraryMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QFunctionLibraryMapping(repositoryContext); + } - private QFunctionLibraryMapping() { + private QFunctionLibraryMapping(@NotNull SqaleRepoContext repositoryContext) { super(QFunctionLibrary.TABLE_NAME, DEFAULT_ALIAS_NAME, - FunctionLibraryType.class, QFunctionLibrary.class); + FunctionLibraryType.class, QFunctionLibrary.class, repositoryContext); } @Override @@ -32,13 +35,6 @@ protected QFunctionLibrary newAliasInstance(String alias) { return new QFunctionLibrary(alias); } - @Override - public ObjectSqlTransformer - createTransformer(SqlTransformerSupport transformerSupport) { - // no special class needed, no additional columns - return new ObjectSqlTransformer<>(transformerSupport, this); - } - @Override public MObject newRowObject() { return new MObject(); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollectionMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollectionMapping.java index 689429112eb..2a47f1b81d6 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollectionMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollectionMapping.java @@ -6,10 +6,11 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.other; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectCollectionType; /** @@ -20,11 +21,13 @@ public class QObjectCollectionMapping public static final String DEFAULT_ALIAS_NAME = "oc"; - public static final QObjectCollectionMapping INSTANCE = new QObjectCollectionMapping(); + public static QObjectCollectionMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QObjectCollectionMapping(repositoryContext); + } - private QObjectCollectionMapping() { + private QObjectCollectionMapping(@NotNull SqaleRepoContext repositoryContext) { super(QObjectCollection.TABLE_NAME, DEFAULT_ALIAS_NAME, - ObjectCollectionType.class, QObjectCollection.class); + ObjectCollectionType.class, QObjectCollection.class, repositoryContext); } @Override @@ -32,13 +35,6 @@ protected QObjectCollection newAliasInstance(String alias) { return new QObjectCollection(alias); } - @Override - public ObjectSqlTransformer - createTransformer(SqlTransformerSupport transformerSupport) { - // no special class needed, no additional columns - return new ObjectSqlTransformer<>(transformerSupport, this); - } - @Override public MObject newRowObject() { return new MObject(); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequenceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequenceMapping.java index d73c213ef1f..7a650d183c2 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequenceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequenceMapping.java @@ -6,10 +6,11 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.other; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.SequenceType; /** @@ -19,11 +20,13 @@ public class QSequenceMapping extends QObjectMapping - createTransformer(SqlTransformerSupport transformerSupport) { - // no special class needed, no additional columns - return new ObjectSqlTransformer<>(transformerSupport, this); - } - @Override public MObject newRowObject() { return new MObject(); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/MReferenceType.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/MReferenceType.java index e436641c746..4baa405a29a 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/MReferenceType.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/MReferenceType.java @@ -6,95 +6,27 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.ref; -import java.util.Objects; -import javax.xml.namespace.QName; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.prism.Containerable; -import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; - /** * Enumeration of various types of reference entities (subtypes of {@link QReference}). - * Each value contains information about concrete Q-type (implying the concrete sub-table) - * and what is mapped to that kind of reference (reference owner + item that stores it). - * - * Implementation notes: * * * Order of values is irrelevant. * * Constant names must match the custom enum type ReferenceType in the database schema. - * - * This class has a bit of bad gravity, as it depends on various schema types. - * TODO: this can perhaps be a job of QObjectReferenceMapping and other ref mappings... */ public enum MReferenceType { // OBJECT REFERENCES - - ARCHETYPE(AssignmentHolderType.class, AssignmentHolderType.F_ARCHETYPE_REF), - - DELEGATED(AssignmentHolderType.class, AssignmentHolderType.F_DELEGATED_REF), - - INCLUDE(ObjectTemplateType.class, ObjectTemplateType.F_INCLUDE_REF), - - PROJECTION(FocusType.class, FocusType.F_LINK_REF), - - OBJECT_CREATE_APPROVER(ObjectType.class, MetadataType.F_CREATE_APPROVER_REF), - - OBJECT_MODIFY_APPROVER(ObjectType.class, MetadataType.F_MODIFY_APPROVER_REF), - - OBJECT_PARENT_ORG(ObjectType.class, ObjectType.F_PARENT_ORG_REF), - - PERSONA(FocusType.class, FocusType.F_PERSONA_REF), - - RESOURCE_BUSINESS_CONFIGURATION_APPROVER( - ResourceType.class, ResourceBusinessConfigurationType.F_APPROVER_REF), - - ROLE_MEMBERSHIP(AssignmentHolderType.class, AssignmentHolderType.F_ROLE_MEMBERSHIP_REF), + ARCHETYPE, + DELEGATED, + INCLUDE, + PROJECTION, + OBJECT_CREATE_APPROVER, + OBJECT_MODIFY_APPROVER, + OBJECT_PARENT_ORG, + PERSONA, + RESOURCE_BUSINESS_CONFIGURATION_APPROVER, + ROLE_MEMBERSHIP, // OTHER REFERENCES - - ASSIGNMENT_CREATE_APPROVER(AssignmentType.class, MetadataType.F_CREATE_APPROVER_REF), - - ASSIGNMENT_MODIFY_APPROVER(AssignmentType.class, MetadataType.F_MODIFY_APPROVER_REF) - - // TODO acc.cert.wi refs - // TODO case.wi refs - ; - - private final Class schemaType; - private final QName itemName; - - MReferenceType( - @NotNull Class schemaType, - @NotNull QName itemName) { - this.schemaType = schemaType; - this.itemName = itemName; - } - - public Class schemaType() { - return schemaType; - } - - public QName itemName() { - return itemName; - } - - // TODO: in old repo it's used by ObjectReferenceMapper.map, will we need it or will Q*Mapping definitions take care of it? - public static MReferenceType getOwnerByQName( - Class typeClass, QName itemName) { - Objects.requireNonNull(typeClass, "Schema type class must not be null"); - Objects.requireNonNull(itemName, "QName must not be null"); - - for (MReferenceType referenceType : values()) { - if (QNameUtil.match(itemName, referenceType.itemName) - && referenceType.schemaType.isAssignableFrom(typeClass)) { - return referenceType; - } - } - - throw new IllegalArgumentException("Can't find reference type for item '" + itemName - + "' in schema type " + typeClass.getName()); - } + ASSIGNMENT_CREATE_APPROVER, + ASSIGNMENT_MODIFY_APPROVER; } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QObjectReferenceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QObjectReferenceMapping.java index 8adb5887ca9..dc19bac5c07 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QObjectReferenceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QObjectReferenceMapping.java @@ -6,22 +6,27 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.ref; +import java.util.Objects; import java.util.function.BiFunction; import com.querydsl.core.types.Predicate; +import org.jetbrains.annotations.NotNull; +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.QObjectTemplate; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; import com.evolveum.midpoint.repo.sqale.qmodel.resource.MResource; import com.evolveum.midpoint.repo.sqale.qmodel.resource.QResource; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; /** * Mapping between {@link QObjectReference} and {@link ObjectReferenceType}. - * The mapping is the same for all subtypes, see different `INSTANCE_*` constants below. - * These instances are not typed properly for non-leaf persistence types like object or focus, - * so mappings for these have adapter methods to change type parameters to ``. + * The mapping is the same for all sub-tables, see various static `get*()` methods below. + * Mapping instances are initialized (`init*()` methods) in {@link QObjectMapping} subclasses. + * Both `init*` and `get*` methods are flexibly parametrized to adapt to the client code. + * Init methods can be called multiple times, only one instance for each sub-tables is created. * * @param query type of the reference owner * @param row type of the reference owner (related to {@link OQ}) @@ -29,31 +34,173 @@ public class QObjectReferenceMapping, OR extends MObject> extends QReferenceMapping, MReference, OQ, OR> { - public static final QObjectReferenceMapping INSTANCE_ARCHETYPE = - new QObjectReferenceMapping<>("m_ref_archetype", "refa"); - public static final QObjectReferenceMapping INSTANCE_DELEGATED = - new QObjectReferenceMapping<>("m_ref_delegated", "refd"); - public static final QObjectReferenceMapping INSTANCE_INCLUDE = - new QObjectReferenceMapping<>("m_ref_include", "refi"); - public static final QObjectReferenceMapping INSTANCE_PROJECTION = - new QObjectReferenceMapping<>("m_ref_projection", "refpj"); - public static final QObjectReferenceMapping INSTANCE_OBJECT_CREATE_APPROVER = - new QObjectReferenceMapping<>("m_ref_object_create_approver", "refca"); - public static final QObjectReferenceMapping INSTANCE_OBJECT_MODIFY_APPROVER = - new QObjectReferenceMapping<>("m_ref_object_modify_approver", "refma"); - public static final QObjectReferenceMapping INSTANCE_OBJECT_PARENT_ORG = - new QObjectReferenceMapping<>("m_ref_object_parent_org", "refpo"); - public static final QObjectReferenceMapping INSTANCE_PERSONA = - new QObjectReferenceMapping<>("m_ref_persona", "refp"); - public static final QObjectReferenceMapping INSTANCE_RESOURCE_BUSINESS_CONFIGURATION_APPROVER = - new QObjectReferenceMapping<>("m_ref_resource_business_configuration_approver", "refrbca"); - public static final QObjectReferenceMapping INSTANCE_ROLE_MEMBERSHIP = - new QObjectReferenceMapping<>("m_ref_role_membership", "refrm"); + public static QObjectReferenceMapping instanceArchetype; + public static QObjectReferenceMapping instanceDelegated; + public static QObjectReferenceMapping instanceInclude; + public static QObjectReferenceMapping instanceProjection; + public static QObjectReferenceMapping instanceObjectCreateApprover; + public static QObjectReferenceMapping instanceObjectModifyApprover; + public static QObjectReferenceMapping instanceObjectParentOrg; + public static QObjectReferenceMapping instancePersona; + public static QObjectReferenceMapping + instanceResourceBusinessConfigurationApprover; + public static QObjectReferenceMapping instanceRoleMembership; + + // region static init/get methods + public static , R extends MObject> + QObjectReferenceMapping initForArchetype(@NotNull SqaleRepoContext repositoryContext) { + if (instanceArchetype == null) { + instanceArchetype = new QObjectReferenceMapping<>( + "m_ref_archetype", "refa", repositoryContext); + } + return getForArchetype(); + } + + public static , R extends MObject> + QObjectReferenceMapping getForArchetype() { + //noinspection unchecked + return (QObjectReferenceMapping) Objects.requireNonNull(instanceArchetype); + } + + public static , R extends MObject> + QObjectReferenceMapping initForDelegated(@NotNull SqaleRepoContext repositoryContext) { + if (instanceDelegated == null) { + instanceDelegated = new QObjectReferenceMapping<>( + "m_ref_delegated", "refd", repositoryContext); + } + return getForDelegated(); + } + + public static , R extends MObject> + QObjectReferenceMapping getForDelegated() { + //noinspection unchecked + return (QObjectReferenceMapping) Objects.requireNonNull(instanceDelegated); + } + + public static QObjectReferenceMapping initForInclude( + @NotNull SqaleRepoContext repositoryContext) { + if (instanceInclude == null) { + instanceInclude = new QObjectReferenceMapping<>( + "m_ref_include", "refi", repositoryContext); + } + return instanceInclude; + } + + public static QObjectReferenceMapping getForInclude() { + return Objects.requireNonNull(instanceInclude); + } + + public static , R extends MObject> QObjectReferenceMapping + initForProjection(@NotNull SqaleRepoContext repositoryContext) { + if (instanceProjection == null) { + instanceProjection = new QObjectReferenceMapping<>( + "m_ref_projection", "refpj", repositoryContext); + } + return getForProjection(); + } + + public static , R extends MObject> + QObjectReferenceMapping getForProjection() { + //noinspection unchecked + return (QObjectReferenceMapping) Objects.requireNonNull(instanceProjection); + } + + public static , R extends MObject> QObjectReferenceMapping + initForObjectCreateApprover(@NotNull SqaleRepoContext repositoryContext) { + if (instanceObjectCreateApprover == null) { + instanceObjectCreateApprover = new QObjectReferenceMapping<>( + "m_ref_object_create_approver", "refca", repositoryContext); + } + return getForObjectCreateApprover(); + } + + public static , R extends MObject> + QObjectReferenceMapping getForObjectCreateApprover() { + //noinspection unchecked + return (QObjectReferenceMapping) Objects.requireNonNull(instanceObjectCreateApprover); + } + + public static , R extends MObject> QObjectReferenceMapping + initForObjectModifyApprover(@NotNull SqaleRepoContext repositoryContext) { + if (instanceObjectModifyApprover == null) { + instanceObjectModifyApprover = new QObjectReferenceMapping<>( + "m_ref_object_modify_approver", "refma", repositoryContext); + } + return getForObjectModifyApprover(); + } + + public static , R extends MObject> + QObjectReferenceMapping getForObjectModifyApprover() { + //noinspection unchecked + return (QObjectReferenceMapping) Objects.requireNonNull(instanceObjectModifyApprover); + } + + public static , R extends MObject> QObjectReferenceMapping + initForObjectParentOrg(@NotNull SqaleRepoContext repositoryContext) { + if (instanceObjectParentOrg == null) { + instanceObjectParentOrg = new QObjectReferenceMapping<>( + "m_ref_object_parent_org", "refpo", repositoryContext); + } + return getForObjectParentOrg(); + } + + public static , R extends MObject> + QObjectReferenceMapping getForObjectParentOrg() { + //noinspection unchecked + return (QObjectReferenceMapping) Objects.requireNonNull(instanceObjectParentOrg); + } + + public static , R extends MObject> + QObjectReferenceMapping initForPersona(@NotNull SqaleRepoContext repositoryContext) { + if (instancePersona == null) { + instancePersona = new QObjectReferenceMapping<>( + "m_ref_persona", "refp", repositoryContext); + } + return getForPersona(); + } + + public static , R extends MObject> + QObjectReferenceMapping getForPersona() { + //noinspection unchecked + return (QObjectReferenceMapping) Objects.requireNonNull(instancePersona); + } + + public static QObjectReferenceMapping + initForResourceBusinessConfigurationApprover(@NotNull SqaleRepoContext repositoryContext) { + if (instanceResourceBusinessConfigurationApprover == null) { + instanceResourceBusinessConfigurationApprover = new QObjectReferenceMapping<>( + "m_ref_resource_business_configuration_approver", "refrbca", repositoryContext); + } + return instanceResourceBusinessConfigurationApprover; + } + + public static QObjectReferenceMapping + getForResourceBusinessConfigurationApprover() { + return Objects.requireNonNull(instanceResourceBusinessConfigurationApprover); + } + + public static , R extends MObject> QObjectReferenceMapping + initForRoleMembership(@NotNull SqaleRepoContext repositoryContext) { + if (instanceRoleMembership == null) { + instanceRoleMembership = new QObjectReferenceMapping<>( + "m_ref_role_membership", "refrm", repositoryContext); + } + return getForRoleMembership(); + } + + public static , R extends MObject> + QObjectReferenceMapping getForRoleMembership() { + //noinspection unchecked + return (QObjectReferenceMapping) Objects.requireNonNull(instanceRoleMembership); + } + // endregion // Sad but true, we can't declare Class>.class, we declare defeat instead. @SuppressWarnings({ "unchecked", "rawtypes" }) - private QObjectReferenceMapping(String tableName, String defaultAliasName) { - super(tableName, defaultAliasName, (Class) QObjectReference.class); + private QObjectReferenceMapping(String tableName, + String defaultAliasName, + @NotNull SqaleRepoContext repositoryContext) { + super(tableName, defaultAliasName, (Class) QObjectReference.class, repositoryContext); } @Override diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReferenceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReferenceMapping.java index cce8979b28c..8828c252dbb 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReferenceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReferenceMapping.java @@ -6,14 +6,17 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.ref; +import java.util.UUID; import java.util.function.BiFunction; import com.querydsl.core.types.Predicate; +import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.prism.Referencable; +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.QOwnedByMapping; import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; @@ -26,21 +29,28 @@ * @param query type of the reference owner * @param row type of the reference owner (related to {@link OQ}) */ -public class QReferenceMapping, R extends MReference, OQ extends FlexibleRelationalPathBase, OR> +public class QReferenceMapping< + Q extends QReference, + R extends MReference, + OQ extends FlexibleRelationalPathBase, + OR> extends SqaleTableMapping implements QOwnedByMapping { // see also subtype specific alias names defined for instances below public static final String DEFAULT_ALIAS_NAME = "ref"; - /** Top level "abstract" reference table, not really needed for normal queries. */ - public static final QReferenceMapping< - QReference, MReference, FlexibleRelationalPathBase, Object> INSTANCE = - new QReferenceMapping<>(QReference.TABLE_NAME, DEFAULT_ALIAS_NAME, QReference.CLASS); + public static QReferenceMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QReferenceMapping<>( + QReference.TABLE_NAME, DEFAULT_ALIAS_NAME, QReference.CLASS, repositoryContext); + } protected QReferenceMapping( - String tableName, String defaultAliasName, Class queryType) { - super(tableName, defaultAliasName, Referencable.class, queryType); + String tableName, + String defaultAliasName, + Class queryType, + @NotNull SqaleRepoContext repositoryContext) { + super(tableName, defaultAliasName, Referencable.class, queryType, repositoryContext); // TODO owner and reference type is not possible to query, probably OK // not sure about this mapping yet, does it make sense to query ref components? @@ -59,12 +69,6 @@ protected Q newAliasInstance(String alias) { return (Q) new QReference<>(MReference.class, alias); } - @Override - public ReferenceSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new ReferenceSqlTransformer<>(transformerSupport, this); - } - /** Defines a contract for creating the reference for the provided owner row. */ public R newRowObject(OR ownerRow) { throw new UnsupportedOperationException( @@ -76,4 +80,21 @@ public BiFunction joinOnPredicate() { throw new UnsupportedOperationException( "joinOnPredicate not supported on abstract reference mapping"); } + + /** + * There is no need to override this, only reference creation is different and that is covered + * by {@link QReferenceMapping#newRowObject(Object)} including setting FK columns. + * All the other columns are based on a single schema type, so there is no variation. + */ + @Override + public R insert(Referencable schemaObject, OR ownerRow, JdbcSession jdbcSession) { + R row = newRowObject(ownerRow); + // row.referenceType is DB generated, must be kept NULL, but it will match referenceType + row.relationId = processCacheableRelation(schemaObject.getRelation()); + row.targetOid = UUID.fromString(schemaObject.getOid()); + row.targetType = schemaTypeToObjectType(schemaObject.getType()); + + insert(row, jdbcSession); + return row; + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/ReferenceSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/ReferenceSqlTransformer.java deleted file mode 100644 index d42fb232724..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/ReferenceSqlTransformer.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.ref; - -import java.util.UUID; - -import com.evolveum.midpoint.prism.Referencable; -import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTransformerBase; -import com.evolveum.midpoint.repo.sqale.qmodel.TransformerForOwnedBy; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; - -/** - * @param type of entity path for the reference table - * @param type of the transformed data, a row bean - * @param row type of the reference owner - */ -public class ReferenceSqlTransformer, R extends MReference, OR> - extends SqaleTransformerBase - implements TransformerForOwnedBy { - - private final QReferenceMapping mapping; - - public ReferenceSqlTransformer( - SqlTransformerSupport transformerSupport, QReferenceMapping mapping) { - super(transformerSupport); - this.mapping = mapping; - } - - @Override - protected QReferenceMapping mapping() { - return mapping; - } - - /** - * There is no need to override this, only reference creation is different and that is covered - * by {@link QReferenceMapping#newRowObject(Object)} including setting FK columns. - * All the other columns are based on a single schema type, so there is no variation. - */ - @Override - public R insert(Referencable schemaObject, OR ownerRow, JdbcSession jdbcSession) { - R row = mapping.newRowObject(ownerRow); - // row.referenceType is DB generated, must be kept NULL, but it will match referenceType - row.relationId = processCacheableRelation(schemaObject.getRelation()); - row.targetOid = UUID.fromString(schemaObject.getOid()); - row.targetType = schemaTypeToObjectType(schemaObject.getType()); - - insert(row, jdbcSession); - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportDataMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportDataMapping.java index 4f4c393fece..e409993c199 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportDataMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportDataMapping.java @@ -8,8 +8,11 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.ReportDataType.F_REPORT_REF; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportDataType; /** @@ -20,11 +23,13 @@ public class QReportDataMapping public static final String DEFAULT_ALIAS_NAME = "repout"; - public static final QReportDataMapping INSTANCE = new QReportDataMapping(); + public static QReportDataMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QReportDataMapping(repositoryContext); + } - private QReportDataMapping() { + private QReportDataMapping(@NotNull SqaleRepoContext repositoryContext) { super(QReportData.TABLE_NAME, DEFAULT_ALIAS_NAME, - ReportDataType.class, QReportData.class); + ReportDataType.class, QReportData.class, repositoryContext); addItemMapping(F_REPORT_REF, refMapper( q -> q.reportRefTargetOid, @@ -38,12 +43,20 @@ protected QReportData newAliasInstance(String alias) { } @Override - public ReportDataSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new ReportDataSqlTransformer(transformerSupport, this); + public MReportData newRowObject() { + return new MReportData(); } @Override - public MReportData newRowObject() { - return new MReportData(); + public @NotNull MReportData toRowObjectWithoutFullObject( + ReportDataType reportData, JdbcSession jdbcSession) { + MReportData row = super.toRowObjectWithoutFullObject(reportData, jdbcSession); + + setReference(reportData.getReportRef(), + o -> row.reportRefTargetOid = o, + t -> row.reportRefTargetType = t, + r -> row.reportRefRelationId = r); + + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportMapping.java index 4e7ecc3a4c0..151312f9b71 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportMapping.java @@ -6,8 +6,11 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.report; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.JasperReportEngineConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportType; @@ -19,11 +22,13 @@ public class QReportMapping public static final String DEFAULT_ALIAS_NAME = "rep"; - public static final QReportMapping INSTANCE = new QReportMapping(); + public static QReportMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QReportMapping(repositoryContext); + } - private QReportMapping() { + private QReportMapping(@NotNull SqaleRepoContext repositoryContext) { super(QReport.TABLE_NAME, DEFAULT_ALIAS_NAME, - ReportType.class, QReport.class); + ReportType.class, QReport.class, repositoryContext); addNestedMapping(ReportType.F_JASPER, JasperReportEngineConfigurationType.class) .addItemMapping(JasperReportEngineConfigurationType.F_ORIENTATION, @@ -38,12 +43,21 @@ protected QReport newAliasInstance(String alias) { } @Override - public ReportSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new ReportSqlTransformer(transformerSupport, this); + public MReport newRowObject() { + return new MReport(); } @Override - public MReport newRowObject() { - return new MReport(); + public @NotNull MReport toRowObjectWithoutFullObject( + ReportType schemaObject, JdbcSession jdbcSession) { + MReport row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); + + JasperReportEngineConfigurationType jasper = schemaObject.getJasper(); + if (jasper != null) { + row.orientation = jasper.getOrientation(); + row.parent = jasper.isParent(); + } + + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/ReportDataSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/ReportDataSqlTransformer.java deleted file mode 100644 index 24a9bbe40da..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/ReportDataSqlTransformer.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.report; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportDataType; - -public class ReportDataSqlTransformer - extends ObjectSqlTransformer { - - public ReportDataSqlTransformer( - SqlTransformerSupport transformerSupport, QReportDataMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MReportData toRowObjectWithoutFullObject( - ReportDataType reportData, JdbcSession jdbcSession) { - MReportData row = super.toRowObjectWithoutFullObject(reportData, jdbcSession); - - setReference(reportData.getReportRef(), - o -> row.reportRefTargetOid = o, - t -> row.reportRefTargetType = t, - r -> row.reportRefRelationId = r); - - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/ReportSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/ReportSqlTransformer.java deleted file mode 100644 index ff2a51847e2..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/ReportSqlTransformer.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.report; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.JasperReportEngineConfigurationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportType; - -public class ReportSqlTransformer - extends ObjectSqlTransformer { - - public ReportSqlTransformer( - SqlTransformerSupport transformerSupport, QReportMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MReport toRowObjectWithoutFullObject( - ReportType schemaObject, JdbcSession jdbcSession) { - MReport row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); - - JasperReportEngineConfigurationType jasper = schemaObject.getJasper(); - if (jasper != null) { - row.orientation = jasper.getOrientation(); - row.parent = jasper.isParent(); - } - - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java index ae5eaafa2b7..289871fadc1 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java @@ -8,9 +8,12 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType.*; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationalStateType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceBusinessConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; @@ -22,16 +25,20 @@ public class QResourceMapping extends QObjectMapping q.businessAdministrativeState)) .addRefMapping(ResourceBusinessConfigurationType.F_APPROVER_REF, - QObjectReferenceMapping.INSTANCE_RESOURCE_BUSINESS_CONFIGURATION_APPROVER); + QObjectReferenceMapping.initForResourceBusinessConfigurationApprover( + repositoryContext)); addNestedMapping(F_OPERATIONAL_STATE, OperationalStateType.class) .addItemMapping(OperationalStateType.F_LAST_AVAILABILITY_STATUS, @@ -49,12 +56,44 @@ protected QResource newAliasInstance(String alias) { } @Override - public ResourceSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new ResourceSqlTransformer(transformerSupport, this); + public MResource newRowObject() { + return new MResource(); } @Override - public MResource newRowObject() { - return new MResource(); + public @NotNull MResource toRowObjectWithoutFullObject( + ResourceType schemaObject, JdbcSession jdbcSession) { + MResource row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); + + ResourceBusinessConfigurationType business = schemaObject.getBusiness(); + if (business != null) { + row.businessAdministrativeState = business.getAdministrativeState(); + } + + OperationalStateType operationalState = schemaObject.getOperationalState(); + if (operationalState != null) { + row.operationalStateLastAvailabilityStatus = + operationalState.getLastAvailabilityStatus(); + } + + setReference(schemaObject.getConnectorRef(), + o -> row.connectorRefTargetOid = o, + t -> row.connectorRefTargetType = t, + r -> row.connectorRefRelationId = r); + + return row; + } + + @Override + public void storeRelatedEntities(@NotNull MResource row, + @NotNull ResourceType schemaObject, @NotNull JdbcSession jdbcSession) { + super.storeRelatedEntities(row, schemaObject, jdbcSession); + + ResourceBusinessConfigurationType business = schemaObject.getBusiness(); + if (business != null) { + storeRefs(row, business.getApproverRef(), + QObjectReferenceMapping.getForResourceBusinessConfigurationApprover(), + jdbcSession); + } } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/ResourceSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/ResourceSqlTransformer.java deleted file mode 100644 index bce4d17a50c..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/ResourceSqlTransformer.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.resource; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationalStateType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceBusinessConfigurationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; - -public class ResourceSqlTransformer - extends ObjectSqlTransformer { - - public ResourceSqlTransformer( - SqlTransformerSupport transformerSupport, QResourceMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MResource toRowObjectWithoutFullObject( - ResourceType schemaObject, JdbcSession jdbcSession) { - MResource row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); - - ResourceBusinessConfigurationType business = schemaObject.getBusiness(); - if (business != null) { - row.businessAdministrativeState = business.getAdministrativeState(); - } - - OperationalStateType operationalState = schemaObject.getOperationalState(); - if (operationalState != null) { - row.operationalStateLastAvailabilityStatus = - operationalState.getLastAvailabilityStatus(); - } - - setReference(schemaObject.getConnectorRef(), - o -> row.connectorRefTargetOid = o, - t -> row.connectorRefTargetType = t, - r -> row.connectorRefRelationId = r); - - return row; - } - - @Override - public void storeRelatedEntities(@NotNull MResource row, - @NotNull ResourceType schemaObject, @NotNull JdbcSession jdbcSession) { - super.storeRelatedEntities(row, schemaObject, jdbcSession); - - ResourceBusinessConfigurationType business = schemaObject.getBusiness(); - if (business != null) { - storeRefs(row, business.getApproverRef(), - QObjectReferenceMapping.INSTANCE_RESOURCE_BUSINESS_CONFIGURATION_APPROVER, - jdbcSession); - } - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/AbstractRoleSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/AbstractRoleSqlTransformer.java deleted file mode 100644 index 18ed811255f..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/AbstractRoleSqlTransformer.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.role; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.focus.FocusSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AutoassignSpecificationType; - -public class AbstractRoleSqlTransformer< - S extends AbstractRoleType, Q extends QAbstractRole, R extends MAbstractRole> - extends FocusSqlTransformer { - - public AbstractRoleSqlTransformer( - SqlTransformerSupport transformerSupport, QAbstractRoleMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull R toRowObjectWithoutFullObject(S abstractRole, JdbcSession jdbcSession) { - R row = super.toRowObjectWithoutFullObject(abstractRole, jdbcSession); - - AutoassignSpecificationType autoassign = abstractRole.getAutoassign(); - if (autoassign != null) { - row.autoAssignEnabled = autoassign.isEnabled(); - } - setPolyString(abstractRole.getDisplayName(), - o -> row.displayNameOrig = o, n -> row.displayNameNorm = n); - row.identifier = abstractRole.getIdentifier(); - row.requestable = abstractRole.isRequestable(); - row.riskLevel = abstractRole.getRiskLevel(); - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/MAbstractRole.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/MAbstractRole.java index 3eed513b4aa..402cfa6f2ee 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/MAbstractRole.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/MAbstractRole.java @@ -13,7 +13,7 @@ */ public class MAbstractRole extends MFocus { - public Boolean autoAssignEnabled; + public Boolean autoAssignEnabled; // autoassign/enabled public String displayNameOrig; public String displayNameNorm; public String identifier; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRoleMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRoleMapping.java index d85e5248d56..84a74a0d7fc 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRoleMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRoleMapping.java @@ -8,11 +8,16 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType.*; +import java.util.List; + import org.jetbrains.annotations.NotNull; +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; +import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentMapping; import com.evolveum.midpoint.repo.sqale.qmodel.focus.QFocusMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AutoassignSpecificationType; /** @@ -28,17 +33,18 @@ public class QAbstractRoleMapping< public static final String DEFAULT_ALIAS_NAME = "ar"; - public static final - QAbstractRoleMapping, MAbstractRole> INSTANCE = - new QAbstractRoleMapping<>(QAbstractRole.TABLE_NAME, DEFAULT_ALIAS_NAME, - AbstractRoleType.class, QAbstractRole.CLASS); + public static QAbstractRoleMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QAbstractRoleMapping<>(QAbstractRole.TABLE_NAME, DEFAULT_ALIAS_NAME, + AbstractRoleType.class, QAbstractRole.CLASS, repositoryContext); + } protected QAbstractRoleMapping( @NotNull String tableName, @NotNull String defaultAliasName, @NotNull Class schemaType, - @NotNull Class queryType) { - super(tableName, defaultAliasName, schemaType, queryType); + @NotNull Class queryType, + @NotNull SqaleRepoContext repositoryContext) { + super(tableName, defaultAliasName, schemaType, queryType, repositoryContext); addNestedMapping(F_AUTOASSIGN, AutoassignSpecificationType.class) .addItemMapping(AutoassignSpecificationType.F_ENABLED, @@ -48,6 +54,10 @@ protected QAbstractRoleMapping( addItemMapping(F_IDENTIFIER, stringMapper(q -> q.identifier)); addItemMapping(F_REQUESTABLE, booleanMapper(q -> q.requestable)); addItemMapping(F_RISK_LEVEL, stringMapper(q -> q.riskLevel)); + + addContainerTableMapping(F_INDUCEMENT, + QAssignmentMapping.initInducement(repositoryContext), + joinOn((o, a) -> o.oid.eq(a.ownerOid))); } @Override @@ -57,8 +67,30 @@ protected Q newAliasInstance(String alias) { } @Override - public AbstractRoleSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new AbstractRoleSqlTransformer<>(transformerSupport, this); + public @NotNull R toRowObjectWithoutFullObject(S abstractRole, JdbcSession jdbcSession) { + R row = super.toRowObjectWithoutFullObject(abstractRole, jdbcSession); + + AutoassignSpecificationType autoassign = abstractRole.getAutoassign(); + if (autoassign != null) { + row.autoAssignEnabled = autoassign.isEnabled(); + } + setPolyString(abstractRole.getDisplayName(), + o -> row.displayNameOrig = o, n -> row.displayNameNorm = n); + row.identifier = abstractRole.getIdentifier(); + row.requestable = abstractRole.isRequestable(); + row.riskLevel = abstractRole.getRiskLevel(); + return row; + } + + @Override + public void storeRelatedEntities( + @NotNull R row, @NotNull S schemaObject, @NotNull JdbcSession jdbcSession) { + super.storeRelatedEntities(row, schemaObject, jdbcSession); + + List inducement = schemaObject.getInducement(); + if (!inducement.isEmpty()) { + inducement.forEach(assignment -> + QAssignmentMapping.getInducement().insert(assignment, row, jdbcSession)); + } } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QArchetypeMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QArchetypeMapping.java index f867f70cbb4..54afcb991b8 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QArchetypeMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QArchetypeMapping.java @@ -6,7 +6,9 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.role; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.xml.ns._public.common.common_3.ArchetypeType; /** @@ -17,11 +19,13 @@ public class QArchetypeMapping public static final String DEFAULT_ALIAS_NAME = "arch"; - public static final QArchetypeMapping INSTANCE = new QArchetypeMapping(); + public static QArchetypeMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QArchetypeMapping(repositoryContext); + } - private QArchetypeMapping() { + private QArchetypeMapping(@NotNull SqaleRepoContext repositoryContext) { super(QArchetype.TABLE_NAME, DEFAULT_ALIAS_NAME, - ArchetypeType.class, QArchetype.class); + ArchetypeType.class, QArchetype.class, repositoryContext); } @Override @@ -29,13 +33,6 @@ protected QArchetype newAliasInstance(String alias) { return new QArchetype(alias); } - @Override - public AbstractRoleSqlTransformer - createTransformer(SqlTransformerSupport transformerSupport) { - // no special class needed, no additional columns - return new AbstractRoleSqlTransformer<>(transformerSupport, this); - } - @Override public MArchetype newRowObject() { return new MArchetype(); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QRoleMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QRoleMapping.java index c7ca6f41ee2..ae9fdfd89cb 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QRoleMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QRoleMapping.java @@ -8,7 +8,10 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType.F_ROLE_TYPE; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; /** @@ -19,11 +22,13 @@ public class QRoleMapping public static final String DEFAULT_ALIAS_NAME = "r"; - public static final QRoleMapping INSTANCE = new QRoleMapping(); + public static QRoleMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QRoleMapping(repositoryContext); + } - private QRoleMapping() { + private QRoleMapping(@NotNull SqaleRepoContext repositoryContext) { super(QRole.TABLE_NAME, DEFAULT_ALIAS_NAME, - RoleType.class, QRole.class); + RoleType.class, QRole.class, repositoryContext); addItemMapping(F_ROLE_TYPE, stringMapper(q -> q.roleType)); } @@ -34,12 +39,17 @@ protected QRole newAliasInstance(String alias) { } @Override - public RoleSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new RoleSqlTransformer(transformerSupport, this); + public MRole newRowObject() { + return new MRole(); } @Override - public MRole newRowObject() { - return new MRole(); + public @NotNull MRole toRowObjectWithoutFullObject( + RoleType schemaObject, JdbcSession jdbcSession) { + MRole row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); + + row.roleType = schemaObject.getRoleType(); + + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QServiceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QServiceMapping.java index 4b8d185fa16..8b7b773a864 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QServiceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QServiceMapping.java @@ -8,7 +8,10 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.ServiceType.F_DISPLAY_ORDER; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.ServiceType; /** @@ -19,11 +22,13 @@ public class QServiceMapping public static final String DEFAULT_ALIAS_NAME = "svc"; - public static final QServiceMapping INSTANCE = new QServiceMapping(); + public static QServiceMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QServiceMapping(repositoryContext); + } - private QServiceMapping() { + private QServiceMapping(@NotNull SqaleRepoContext repositoryContext) { super(QService.TABLE_NAME, DEFAULT_ALIAS_NAME, - ServiceType.class, QService.class); + ServiceType.class, QService.class, repositoryContext); addItemMapping(F_DISPLAY_ORDER, integerMapper(q -> q.displayOrder)); } @@ -34,12 +39,17 @@ protected QService newAliasInstance(String alias) { } @Override - public ServiceSqlTransformer createTransformer(SqlTransformerSupport transformerSupport) { - return new ServiceSqlTransformer(transformerSupport, this); + public MService newRowObject() { + return new MService(); } @Override - public MService newRowObject() { - return new MService(); + public @NotNull MService toRowObjectWithoutFullObject( + ServiceType schemaObject, JdbcSession jdbcSession) { + MService row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); + + row.displayOrder = schemaObject.getDisplayOrder(); + + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/RoleSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/RoleSqlTransformer.java deleted file mode 100644 index 60dcbf2db88..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/RoleSqlTransformer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.role; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; - -public class RoleSqlTransformer - extends AbstractRoleSqlTransformer { - - public RoleSqlTransformer( - SqlTransformerSupport transformerSupport, QRoleMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MRole toRowObjectWithoutFullObject( - RoleType schemaObject, JdbcSession jdbcSession) { - MRole row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); - - row.roleType = schemaObject.getRoleType(); - - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/ServiceSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/ServiceSqlTransformer.java deleted file mode 100644 index 5885dfe1c69..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/ServiceSqlTransformer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.role; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ServiceType; - -public class ServiceSqlTransformer - extends AbstractRoleSqlTransformer { - - public ServiceSqlTransformer( - SqlTransformerSupport transformerSupport, QServiceMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MService toRowObjectWithoutFullObject( - ServiceType schemaObject, JdbcSession jdbcSession) { - MService row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); - - row.displayOrder = schemaObject.getDisplayOrder(); - - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadowMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadowMapping.java index 7ff572cc48b..f6f655e5f5e 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadowMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadowMapping.java @@ -8,8 +8,12 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType.*; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; /** @@ -20,10 +24,13 @@ public class QShadowMapping public static final String DEFAULT_ALIAS_NAME = "sh"; - public static final QShadowMapping INSTANCE = new QShadowMapping(); + public static QShadowMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QShadowMapping(repositoryContext); + } - private QShadowMapping() { - super(QShadow.TABLE_NAME, DEFAULT_ALIAS_NAME, ShadowType.class, QShadow.class); + private QShadowMapping(@NotNull SqaleRepoContext repositoryContext) { + super(QShadow.TABLE_NAME, DEFAULT_ALIAS_NAME, + ShadowType.class, QShadow.class, repositoryContext); addItemMapping(ShadowType.F_OBJECT_CLASS, uriMapper(q -> q.objectClassId)); addItemMapping(F_RESOURCE_REF, refMapper( @@ -51,13 +58,34 @@ protected QShadow newAliasInstance(String alias) { } @Override - public ShadowSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new ShadowSqlTransformer(transformerSupport, this); + public MShadow newRowObject() { + return new MShadow(); } @Override - public MShadow newRowObject() { - return new MShadow(); + public @NotNull MShadow toRowObjectWithoutFullObject( + ShadowType shadow, JdbcSession jdbcSession) { + MShadow row = super.toRowObjectWithoutFullObject(shadow, jdbcSession); + + row.objectClassId = processCacheableUri(shadow.getObjectClass()); + setReference(shadow.getResourceRef(), + o -> row.resourceRefTargetOid = o, + t -> row.resourceRefTargetType = t, + r -> row.resourceRefRelationId = r); + row.intent = shadow.getIntent(); + row.kind = shadow.getKind(); +// row.attemptNumber = shadow.att; TODO not set in RShadow, probably just with deltas? Where does it come from? + row.dead = shadow.isDead(); + row.exist = shadow.isExists(); + row.fullSynchronizationTimestamp = + MiscUtil.asInstant(shadow.getFullSynchronizationTimestamp()); + row.pendingOperationCount = shadow.getPendingOperation().size(); + row.primaryIdentifierValue = shadow.getPrimaryIdentifierValue(); + row.synchronizationSituation = shadow.getSynchronizationSituation(); + row.synchronizationTimestamp = MiscUtil.asInstant(shadow.getSynchronizationTimestamp()); + + // TODO extension attributes + // copyExtensionOrAttributesFromJAXB(jaxb.getAttributes().asPrismContainerValue(), repo, repositoryContext, RObjectExtensionType.ATTRIBUTES, generatorResult); + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/ShadowSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/ShadowSqlTransformer.java deleted file mode 100644 index 1e229b4dae9..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/ShadowSqlTransformer.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.shadow; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; - -public class ShadowSqlTransformer - extends ObjectSqlTransformer { - - public ShadowSqlTransformer( - SqlTransformerSupport transformerSupport, QShadowMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MShadow toRowObjectWithoutFullObject( - ShadowType shadow, JdbcSession jdbcSession) { - MShadow row = super.toRowObjectWithoutFullObject(shadow, jdbcSession); - - row.objectClassId = processCacheableUri(shadow.getObjectClass()); - setReference(shadow.getResourceRef(), - o -> row.resourceRefTargetOid = o, - t -> row.resourceRefTargetType = t, - r -> row.resourceRefRelationId = r); - row.intent = shadow.getIntent(); - row.kind = shadow.getKind(); -// row.attemptNumber = shadow.att; TODO not set in RShadow, probably just with deltas? Where does it come from? - row.dead = shadow.isDead(); - row.exist = shadow.isExists(); - row.fullSynchronizationTimestamp = - MiscUtil.asInstant(shadow.getFullSynchronizationTimestamp()); - row.pendingOperationCount = shadow.getPendingOperation().size(); - row.primaryIdentifierValue = shadow.getPrimaryIdentifierValue(); - row.synchronizationSituation = shadow.getSynchronizationSituation(); - row.synchronizationTimestamp = MiscUtil.asInstant(shadow.getSynchronizationTimestamp()); - - // TODO extension attributes - // copyExtensionOrAttributesFromJAXB(jaxb.getAttributes().asPrismContainerValue(), repo, repositoryContext, RObjectExtensionType.ATTRIBUTES, generatorResult); - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicyMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicyMapping.java index 001f458c3b7..af1e42c0853 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicyMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicyMapping.java @@ -6,10 +6,11 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.system; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.SecurityPolicyType; /** @@ -20,11 +21,13 @@ public class QSecurityPolicyMapping public static final String DEFAULT_ALIAS_NAME = "sp"; - public static final QSecurityPolicyMapping INSTANCE = new QSecurityPolicyMapping(); + public static QSecurityPolicyMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QSecurityPolicyMapping(repositoryContext); + } - private QSecurityPolicyMapping() { + private QSecurityPolicyMapping(@NotNull SqaleRepoContext repositoryContext) { super(QSecurityPolicy.TABLE_NAME, DEFAULT_ALIAS_NAME, - SecurityPolicyType.class, QSecurityPolicy.class); + SecurityPolicyType.class, QSecurityPolicy.class, repositoryContext); } @Override @@ -32,13 +35,6 @@ protected QSecurityPolicy newAliasInstance(String alias) { return new QSecurityPolicy(alias); } - @Override - public ObjectSqlTransformer - createTransformer(SqlTransformerSupport transformerSupport) { - // no special class needed, no additional columns - return new ObjectSqlTransformer<>(transformerSupport, this); - } - @Override public MObject newRowObject() { return new MObject(); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfigurationMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfigurationMapping.java index b6f5b30e143..e54d99c287e 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfigurationMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfigurationMapping.java @@ -6,10 +6,11 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.system; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; /** @@ -20,11 +21,13 @@ public class QSystemConfigurationMapping public static final String DEFAULT_ALIAS_NAME = "sc"; - public static final QSystemConfigurationMapping INSTANCE = new QSystemConfigurationMapping(); + public static QSystemConfigurationMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QSystemConfigurationMapping(repositoryContext); + } - private QSystemConfigurationMapping() { + private QSystemConfigurationMapping(@NotNull SqaleRepoContext repositoryContext) { super(QSystemConfiguration.TABLE_NAME, DEFAULT_ALIAS_NAME, - SystemConfigurationType.class, QSystemConfiguration.class); + SystemConfigurationType.class, QSystemConfiguration.class, repositoryContext); } @Override @@ -32,13 +35,6 @@ protected QSystemConfiguration newAliasInstance(String alias) { return new QSystemConfiguration(alias); } - @Override - public ObjectSqlTransformer - createTransformer(SqlTransformerSupport transformerSupport) { - // no special class needed, no additional columns - return new ObjectSqlTransformer<>(transformerSupport, this); - } - @Override public MObject newRowObject() { return new MObject(); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicyMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicyMapping.java index 3b4d87a0277..94d75b731ae 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicyMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicyMapping.java @@ -6,10 +6,11 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.system; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.ValuePolicyType; /** @@ -20,11 +21,13 @@ public class QValuePolicyMapping public static final String DEFAULT_ALIAS_NAME = "vp"; - public static final QValuePolicyMapping INSTANCE = new QValuePolicyMapping(); + public static QValuePolicyMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QValuePolicyMapping(repositoryContext); + } - private QValuePolicyMapping() { + private QValuePolicyMapping(@NotNull SqaleRepoContext repositoryContext) { super(QValuePolicy.TABLE_NAME, DEFAULT_ALIAS_NAME, - ValuePolicyType.class, QValuePolicy.class); + ValuePolicyType.class, QValuePolicy.class, repositoryContext); } @Override @@ -32,13 +35,6 @@ protected QValuePolicy newAliasInstance(String alias) { return new QValuePolicy(alias); } - @Override - public ObjectSqlTransformer - createTransformer(SqlTransformerSupport transformerSupport) { - // no special class needed, no additional columns - return new ObjectSqlTransformer<>(transformerSupport, this); - } - @Override public MObject newRowObject() { return new MObject(); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTaskMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTaskMapping.java index 54d94df64d6..acd439e3ff8 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTaskMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTaskMapping.java @@ -6,8 +6,12 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.task; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; /** @@ -18,11 +22,13 @@ public class QTaskMapping public static final String DEFAULT_ALIAS_NAME = "t"; - public static final QTaskMapping INSTANCE = new QTaskMapping(); + public static QTaskMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QTaskMapping(repositoryContext); + } - private QTaskMapping() { + private QTaskMapping(@NotNull SqaleRepoContext repositoryContext) { super(QTask.TABLE_NAME, DEFAULT_ALIAS_NAME, - TaskType.class, QTask.class); + TaskType.class, QTask.class, repositoryContext); addItemMapping(TaskType.F_TASK_IDENTIFIER, stringMapper(q -> q.taskIdentifier)); addItemMapping(TaskType.F_BINDING, enumMapper(q -> q.binding)); @@ -59,13 +65,40 @@ protected QTask newAliasInstance(String alias) { } @Override - public TaskSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new TaskSqlTransformer(transformerSupport, this); + public MTask newRowObject() { + return new MTask(); } @Override - public MTask newRowObject() { - return new MTask(); + public @NotNull MTask toRowObjectWithoutFullObject( + TaskType task, JdbcSession jdbcSession) { + MTask row = super.toRowObjectWithoutFullObject(task, jdbcSession); + + row.taskIdentifier = task.getTaskIdentifier(); + row.binding = task.getBinding(); + row.category = task.getCategory(); + row.completionTimestamp = MiscUtil.asInstant(task.getCompletionTimestamp()); + row.executionStatus = task.getExecutionStatus(); +// row.fullResult = TODO + row.handlerUriId = processCacheableUri(task.getHandlerUri()); + row.lastRunStartTimestamp = MiscUtil.asInstant(task.getLastRunStartTimestamp()); + row.lastRunFinishTimestamp = MiscUtil.asInstant(task.getLastRunFinishTimestamp()); + row.node = task.getNode(); + setReference(task.getObjectRef(), + o -> row.objectRefTargetOid = o, + t -> row.objectRefTargetType = t, + r -> row.objectRefRelationId = r); + setReference(task.getOwnerRef(), + o -> row.ownerRefTargetOid = o, + t -> row.ownerRefTargetType = t, + r -> row.ownerRefRelationId = r); + row.parent = task.getParent(); + row.recurrence = task.getRecurrence(); + row.resultStatus = task.getResultStatus(); + row.threadStopAction = task.getThreadStopAction(); + row.waitingReason = task.getWaitingReason(); + row.dependentTaskIdentifiers = task.getDependent().toArray(String[]::new); + + return row; } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/TaskSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/TaskSqlTransformer.java deleted file mode 100644 index 9058d52ddde..00000000000 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/TaskSqlTransformer.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqale.qmodel.task; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; - -public class TaskSqlTransformer extends ObjectSqlTransformer { - - public TaskSqlTransformer(SqlTransformerSupport transformerSupport, QTaskMapping mapping) { - super(transformerSupport, mapping); - } - - @Override - public @NotNull MTask toRowObjectWithoutFullObject( - TaskType task, JdbcSession jdbcSession) { - MTask row = super.toRowObjectWithoutFullObject(task, jdbcSession); - - row.taskIdentifier = task.getTaskIdentifier(); - row.binding = task.getBinding(); - row.category = task.getCategory(); - row.completionTimestamp = MiscUtil.asInstant(task.getCompletionTimestamp()); - row.executionStatus = task.getExecutionStatus(); -// row.fullResult = TODO - row.handlerUriId = processCacheableUri(task.getHandlerUri()); - row.lastRunStartTimestamp = MiscUtil.asInstant(task.getLastRunStartTimestamp()); - row.lastRunFinishTimestamp = MiscUtil.asInstant(task.getLastRunFinishTimestamp()); - row.node = task.getNode(); - setReference(task.getObjectRef(), - o -> row.objectRefTargetOid = o, - t -> row.objectRefTargetType = t, - r -> row.objectRefRelationId = r); - setReference(task.getOwnerRef(), - o -> row.ownerRefTargetOid = o, - t -> row.ownerRefTargetType = t, - r -> row.ownerRefRelationId = r); - row.parent = task.getParent(); - row.recurrence = task.getRecurrence(); - row.resultStatus = task.getResultStatus(); - row.threadStopAction = task.getThreadStopAction(); - row.waitingReason = task.getWaitingReason(); - row.dependentTaskIdentifiers = task.getDependent().toArray(String[]::new); - - return row; - } -} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/ContainerTableUpdateContext.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/ContainerTableUpdateContext.java index e65089b48cd..4162e18a188 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/ContainerTableUpdateContext.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/ContainerTableUpdateContext.java @@ -16,13 +16,10 @@ import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainerMapping; /** - * Update context for owned containers stored in tables. + * Update context for multi-value containers stored in separate table. * This can be owned by the root object or another container. - * TODO - this is theory, before implementation: - * Updates are collected as the modifications are processed and then executed by the root context. - * Inserts are executed immediately to allow nested inserts (e.g. container inside the container). * - * @param schema type of the object stored in the owned (child) table + * @param schema type of the container stored in the owned table * @param type of entity path for the owned (child) table * @param row type related to the {@link Q} * @param owner row type diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/NestedContainerUpdateContext.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/NestedContainerUpdateContext.java index dac0ac2f7b9..cfed67e7bf7 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/NestedContainerUpdateContext.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/NestedContainerUpdateContext.java @@ -16,7 +16,7 @@ /** * Update context for nested containers stored in the same table used by the parent context. * - * @param schema type of the object mapped by nested mapping + * @param schema type of the container mapped by the nested mapping * @param entity query type that holds the data for the mapped attributes * @param row type related to the {@link Q} */ diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java index b2df16978a3..2d137a9bccd 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java @@ -14,16 +14,16 @@ import com.querydsl.core.types.Path; import com.querydsl.sql.dml.SQLUpdateClause; -import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy; import com.evolveum.midpoint.repo.sqale.ContainerValueIdGenerator; -import com.evolveum.midpoint.repo.sqale.SqaleTransformerSupport; +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.SqaleUtils; import com.evolveum.midpoint.repo.sqale.delta.DelegatingItemDeltaProcessor; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.ObjectSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.repo.sqlbase.RepositoryException; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; @@ -32,8 +32,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; /** - * TODO - * Adds execute that processes the modifications and finalizes the update of root entity. + * Root context of the update context tree, see {@link SqaleUpdateContext} for more information. * * @param schema type * @param type of entity path @@ -43,20 +42,19 @@ public class RootUpdateContext, R ext extends SqaleUpdateContext { private final S object; - protected final QueryTableMapping mapping; + protected final QObjectMapping mapping; private final Q rootPath; private final SQLUpdateClause update; private final int objectVersion; private ContainerValueIdGenerator cidGenerator; - public RootUpdateContext(SqaleTransformerSupport transformerSupport, + public RootUpdateContext(SqaleRepoContext repositoryContext, JdbcSession jdbcSession, S object, R rootRow) { - super(transformerSupport, jdbcSession, rootRow); + super(repositoryContext, jdbcSession, rootRow); this.object = object; - mapping = transformerSupport.sqlRepoContext() - .getMappingBySchemaType(SqaleUtils.getClass(object)); + mapping = repositoryContext.getMappingBySchemaType(SqaleUtils.getClass(object)); rootPath = mapping.defaultAlias(); objectVersion = objectVersionAsInt(object); // root context always updates, at least version and full object, so we can create it early @@ -102,7 +100,7 @@ public QueryTableMapping mapping() { } } - transformerSupport.normalizeAllRelations(prismObject); + repositoryContext().normalizeAllRelations(prismObject); finishExecution(); return modifications; @@ -111,11 +109,33 @@ public QueryTableMapping mapping() { private void processModification(ItemDelta modification) throws RepositoryException, SchemaException { cidGenerator.processModification(modification); + resolveContainerIdsForValuesToDelete(modification); modification.applyTo(getPrismObject()); new DelegatingItemDeltaProcessor(this).process(modification); } + private void resolveContainerIdsForValuesToDelete(ItemDelta modification) { + if (!modification.isDelete()) { + return; + } + + PrismContainer container = + getPrismObject().findContainer(modification.getPath()); + if (container != null) { + for (PrismValue value : modification.getValuesToDelete()) { + //noinspection unchecked + PrismContainerValue pcv = (PrismContainerValue) value; + if (pcv.getId() == null) { + PrismContainerValue existingValue = container.findValue( + pcv, EquivalenceStrategy.REAL_VALUE_CONSIDER_DIFFERENT_IDS); + // We will set CID and use that for DB updates. + pcv.setId(existingValue.getId()); + } + } + } + } + /** * Executes all necessary SQL updates (including sub-entity inserts/deletes) * for the enclosed {@link #object}. @@ -127,10 +147,7 @@ protected void finishExecutionOwn() throws SchemaException, RepositoryException update.set(rootPath.version, newVersion); update.set(rootPath.containerIdSeq, cidGenerator.lastUsedId() + 1); - - ObjectSqlTransformer transformer = - (ObjectSqlTransformer) mapping.createTransformer(transformerSupport); - update.set(rootPath.fullObject, transformer.createFullObject(object)); + update.set(rootPath.fullObject, mapping.createFullObject(object)); long rows = update.execute(); if (rows != 1) { diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/SqaleUpdateContext.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/SqaleUpdateContext.java index 12e5515ddc3..7cc1b9823f4 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/SqaleUpdateContext.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/SqaleUpdateContext.java @@ -8,17 +8,15 @@ import java.util.LinkedHashMap; import java.util.Map; -import javax.xml.namespace.QName; import com.querydsl.core.types.Path; import com.evolveum.midpoint.prism.path.ItemName; import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.repo.sqale.SqaleTransformerSupport; +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.delta.ItemDeltaValueProcessor; import com.evolveum.midpoint.repo.sqale.delta.item.UriItemDeltaProcessor; import com.evolveum.midpoint.repo.sqale.qmodel.QOwnedByMapping; -import com.evolveum.midpoint.repo.sqale.qmodel.TransformerForOwnedBy; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.repo.sqlbase.RepositoryException; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryModelMapping; @@ -61,7 +59,7 @@ public abstract class SqaleUpdateContext> subcontexts = new LinkedHashMap<>(); public SqaleUpdateContext( - SqaleTransformerSupport sqlTransformerSupport, + SqaleRepoContext repositoryContext, JdbcSession jdbcSession, R row) { - this.transformerSupport = sqlTransformerSupport; + this.repositoryContext = repositoryContext; this.jdbcSession = jdbcSession; this.row = row; @@ -94,21 +92,13 @@ public SqaleUpdateContext( R row) { this.parentContext = parentContext; // registering this with parent context must happen outside of constructor! - this.transformerSupport = parentContext.transformerSupport; + this.repositoryContext = parentContext.repositoryContext; this.jdbcSession = parentContext.jdbcSession(); this.row = row; } - public SqaleTransformerSupport transformerSupport() { - return transformerSupport; - } - - public Integer processCacheableRelation(QName relation) { - return transformerSupport.processCacheableRelation(relation); - } - - public Integer processCacheableUri(String uri) { - return transformerSupport.processCacheableUri(uri); + public SqaleRepoContext repositoryContext() { + return repositoryContext; } public JdbcSession jdbcSession() { @@ -127,9 +117,7 @@ public R row() { @SuppressWarnings("UnusedReturnValue") public TR insertOwnedRow(QOwnedByMapping mapping, TS schemaObject) { - TransformerForOwnedBy transformer = - mapping.createTransformer(transformerSupport()); - return transformer.insert(schemaObject, row, jdbcSession); + return mapping.insert(schemaObject, row, jdbcSession); } public SqaleUpdateContext getSubcontext(ItemPath itemPath) { diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java index 0a306423b86..b4e51abf1cf 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java @@ -24,9 +24,7 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest; import com.evolveum.midpoint.repo.sqale.qmodel.accesscert.MAccessCertificationDefinition; import com.evolveum.midpoint.repo.sqale.qmodel.accesscert.QAccessCertificationDefinition; -import com.evolveum.midpoint.repo.sqale.qmodel.assignment.MAssignmentReference; -import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentReference; -import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentReferenceMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.assignment.*; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainer; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainer; @@ -48,6 +46,8 @@ import com.evolveum.midpoint.repo.sqale.qmodel.report.QReportData; import com.evolveum.midpoint.repo.sqale.qmodel.resource.MResource; import com.evolveum.midpoint.repo.sqale.qmodel.resource.QResource; +import com.evolveum.midpoint.repo.sqale.qmodel.role.MArchetype; +import com.evolveum.midpoint.repo.sqale.qmodel.role.QArchetype; import com.evolveum.midpoint.repo.sqale.qmodel.shadow.MShadow; import com.evolveum.midpoint.repo.sqale.qmodel.shadow.QShadow; import com.evolveum.midpoint.repo.sqale.qmodel.system.QSystemConfiguration; @@ -75,14 +75,15 @@ public void test100AddNamedUserWithoutOidWorksOk() .name(userName); when("adding it to the repository"); - repositoryService.addObject(userType.asPrismObject(), null, result); + String returnedOid = repositoryService.addObject(userType.asPrismObject(), null, result); then("operation is successful and user row for it is created"); assertThatOperationResult(result).isSuccess(); + assertThat(returnedOid).isEqualTo(userType.getOid()); QUser u = aliasFor(QUser.class); MUser row = selectOne(u, u.nameOrig.eq(userName)); - assertThat(row.oid).isNotNull(); + assertThat(row.oid).isEqualTo(UUID.fromString(returnedOid)); assertThat(row.nameNorm).isNotNull(); // normalized name is stored assertThat(row.version).isEqualTo(1); // initial version is set // read-only column with value generated/stored in the database @@ -292,7 +293,7 @@ public void test205AddObjectWithMultivalueRefs() assertThat(userRow.containerIdSeq).isEqualTo(1); // cid sequence is in initial state UUID userOid = UUID.fromString(user.getOid()); - QObjectReference or = QObjectReferenceMapping.INSTANCE_PROJECTION.defaultAlias(); + QObjectReference or = QObjectReferenceMapping.getForProjection().defaultAlias(); List projectionRefs = select(or, or.ownerOid.eq(userOid)); assertThat(projectionRefs).hasSize(2) .allMatch(rRow -> rRow.referenceType == MReferenceType.PROJECTION) @@ -334,7 +335,7 @@ public void test206AddObjectWithMultivalueRefsOnAssignment() assertThat(userRow.oid).isNotNull(); QAssignmentReference ar = - QAssignmentReferenceMapping.INSTANCE_ASSIGNMENT_CREATE_APPROVER.defaultAlias(); + QAssignmentReferenceMapping.getForAssignmentCreateApprover().defaultAlias(); List projectionRefs = select(ar, ar.ownerOid.eq(userRow.oid)); assertThat(projectionRefs).hasSize(2) .allMatch(rRow -> rRow.referenceType == MReferenceType.ASSIGNMENT_CREATE_APPROVER) @@ -377,7 +378,7 @@ public void test291DuplicateCidInDifferentContainersIsCaughtByRepo() { // region insertion of various types - // this test covers function of ObjectSqlTransformer and all the basic object fields + // this test covers function of QObjectMapping and all the basic object fields @Test public void test800SystemConfigurationBasicObjectAttributes() throws Exception { OperationResult result = createOperationResult(); @@ -402,7 +403,7 @@ public void test800SystemConfigurationBasicObjectAttributes() throws Exception { .creatorRef(creatorRefOid.toString(), UserType.COMPLEX_TYPE, relation1) .createChannel("create-channel") .createTimestamp(MiscUtil.asXMLGregorianCalendar(1L)) - .modifierRef(modifierRefOid.toString(), ServiceType.COMPLEX_TYPE, relation2) + .modifierRef(modifierRefOid.toString(), UserType.COMPLEX_TYPE, relation2) .modifyChannel("modify-channel") .modifyTimestamp(MiscUtil.asXMLGregorianCalendar(2L))); @@ -433,7 +434,7 @@ public void test800SystemConfigurationBasicObjectAttributes() throws Exception { assertCachedUri(row.createChannelId, "create-channel"); assertThat(row.createTimestamp).isEqualTo(Instant.ofEpochMilli(1)); assertThat(row.modifierRefTargetOid).isEqualTo(modifierRefOid); - assertThat(row.modifierRefTargetType).isEqualTo(MObjectType.SERVICE); + assertThat(row.modifierRefTargetType).isEqualTo(MObjectType.USER); assertCachedUri(row.modifierRefRelationId, relation2); assertCachedUri(row.modifyChannelId, "modify-channel"); assertThat(row.modifyTimestamp).isEqualTo(Instant.ofEpochMilli(2)); @@ -536,6 +537,114 @@ public void test802ContainerOperationExecution() throws Exception { // this time we didn't test assigned CID or CID SEQ value on owner (see test801) } + @Test + public void test803ContainerAssignment() throws Exception { + OperationResult result = createOperationResult(); + + given("object with assignments"); + String objectName = "sc" + getTestNumber(); + UUID orgRefOid = UUID.randomUUID(); + UUID targetRefOid = UUID.randomUUID(); + UUID tenantRefOid = UUID.randomUUID(); + UUID resourceRefOid = UUID.randomUUID(); + UUID creatorRefOid = UUID.randomUUID(); + UUID modifierRefOid = UUID.randomUUID(); + QName relation1 = QName.valueOf("{https://random.org/ns}random-rel-1"); + QName relation2 = QName.valueOf("{https://random.org/ns}random-rel-2"); + SystemConfigurationType object = new SystemConfigurationType(prismContext) + .name(objectName) + .assignment(new AssignmentType(prismContext) + .lifecycleState("lifecycle-state") + .order(47) + .orgRef(orgRefOid.toString(), OrgType.COMPLEX_TYPE, relation1) + .targetRef(targetRefOid.toString(), RoleType.COMPLEX_TYPE, relation2) + .tenantRef(tenantRefOid.toString(), OrgType.COMPLEX_TYPE, relation2) + // TODO extId, extOid, ext? + .policySituation("policy-situation-1") + .policySituation("policy-situation-2") + .construction(new ConstructionType() + .resourceRef(resourceRefOid.toString(), + ResourceType.COMPLEX_TYPE, relation1)) + .activation(new ActivationType() + .administrativeStatus(ActivationStatusType.ENABLED) + .effectiveStatus(ActivationStatusType.DISABLED) + .enableTimestamp(MiscUtil.asXMLGregorianCalendar(3L)) + .disableTimestamp(MiscUtil.asXMLGregorianCalendar(4L)) + .disableReason("disable-reason") + .validityStatus(TimeIntervalStatusType.IN) + .validFrom(MiscUtil.asXMLGregorianCalendar(5L)) + .validTo(MiscUtil.asXMLGregorianCalendar(6L)) + .validityChangeTimestamp(MiscUtil.asXMLGregorianCalendar(7L)) + .archiveTimestamp(MiscUtil.asXMLGregorianCalendar(8L))) + .metadata(new MetadataType() + // multi-value approver refs are tested elsewhere + .creatorRef(creatorRefOid.toString(), UserType.COMPLEX_TYPE, relation1) + .createChannel("create-channel") + .createTimestamp(MiscUtil.asXMLGregorianCalendar(1L)) + .modifierRef(modifierRefOid.toString(), UserType.COMPLEX_TYPE, relation2) + .modifyChannel("modify-channel") + .modifyTimestamp(MiscUtil.asXMLGregorianCalendar(2L)))) + // one more just to see it stores multiple assignments + .assignment(new AssignmentType().order(1)); + + when("adding it to the repository"); + repositoryService.addObject(object.asPrismObject(), null, result); + + then("it is stored and rows to child tables are inserted"); + assertThatOperationResult(result).isSuccess(); + + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); + List aRows = select(a, a.ownerOid.eq(UUID.fromString(object.getOid()))); + assertThat(aRows).hasSize(2) + .allMatch(ar -> ar.orderValue != null); + + MAssignment row = aRows.stream() + .filter(ar -> ar.orderValue == 47) + .findFirst().orElseThrow(); + + assertThat(row.lifecycleState).isEqualTo("lifecycle-state"); + assertThat(row.orderValue).isEqualTo(47); + assertThat(row.orgRefTargetOid).isEqualTo(orgRefOid); + assertThat(row.orgRefTargetType).isEqualTo(MObjectType.ORG); + assertCachedUri(row.orgRefRelationId, relation1); + assertThat(row.targetRefTargetOid).isEqualTo(targetRefOid); + assertThat(row.targetRefTargetType).isEqualTo(MObjectType.ROLE); + assertCachedUri(row.targetRefRelationId, relation2); + assertThat(row.tenantRefTargetOid).isEqualTo(tenantRefOid); + assertThat(row.tenantRefTargetType).isEqualTo(MObjectType.ORG); + assertCachedUri(row.tenantRefRelationId, relation2); + // complex DB columns + // TODO EXT + assertThat(resolveCachedUriIds(row.policySituations)) + .containsExactlyInAnyOrder("policy-situation-1", "policy-situation-2"); + // construction + assertThat(row.resourceRefTargetOid).isEqualTo(resourceRefOid); + assertThat(row.resourceRefTargetType).isEqualTo(MObjectType.RESOURCE); + assertCachedUri(row.resourceRefRelationId, relation1); + // activation + assertThat(row.administrativeStatus).isEqualTo(ActivationStatusType.ENABLED); + assertThat(row.effectiveStatus).isEqualTo(ActivationStatusType.DISABLED); + assertThat(row.enableTimestamp).isEqualTo(Instant.ofEpochMilli(3)); + assertThat(row.disableTimestamp).isEqualTo(Instant.ofEpochMilli(4)); + assertThat(row.disableReason).isEqualTo("disable-reason"); + assertThat(row.validityStatus).isEqualTo(TimeIntervalStatusType.IN); + assertThat(row.validFrom).isEqualTo(Instant.ofEpochMilli(5)); + assertThat(row.validTo).isEqualTo(Instant.ofEpochMilli(6)); + assertThat(row.validityChangeTimestamp).isEqualTo(Instant.ofEpochMilli(7)); + assertThat(row.archiveTimestamp).isEqualTo(Instant.ofEpochMilli(8)); + // metadata + assertThat(row.creatorRefTargetOid).isEqualTo(creatorRefOid); + assertThat(row.creatorRefTargetType).isEqualTo(MObjectType.USER); + assertCachedUri(row.creatorRefRelationId, relation1); + assertCachedUri(row.createChannelId, "create-channel"); + assertThat(row.createTimestamp).isEqualTo(Instant.ofEpochMilli(1)); + assertThat(row.modifierRefTargetOid).isEqualTo(modifierRefOid); + assertThat(row.modifierRefTargetType).isEqualTo(MObjectType.USER); + assertCachedUri(row.modifierRefRelationId, relation2); + assertCachedUri(row.modifyChannelId, "modify-channel"); + assertThat(row.modifyTimestamp).isEqualTo(Instant.ofEpochMilli(2)); + } + @Test public void test808LookupTable() throws Exception { OperationResult result = createOperationResult(); @@ -587,10 +696,6 @@ public void test808LookupTable() throws Exception { assertThat(containerRow.key).isEqualTo("row3"); } - // TODO test for object's related entities? - // - trigger - // - operation execution - @Test public void test810ResourceAndItsBusinessApproverReferences() throws Exception { OperationResult result = createOperationResult(); @@ -630,7 +735,7 @@ public void test810ResourceAndItsBusinessApproverReferences() throws Exception { assertCachedUri(row.connectorRefRelationId, connectorRelation); QObjectReference ref = QObjectReferenceMapping - .INSTANCE_RESOURCE_BUSINESS_CONFIGURATION_APPROVER.defaultAlias(); + .getForResourceBusinessConfigurationApprover().defaultAlias(); List refs = select(ref, ref.ownerOid.eq(row.oid)); assertThat(refs).hasSize(2); @@ -795,7 +900,7 @@ public void test818Shadow() throws Exception { assertThat(row.synchronizationTimestamp).isEqualTo(Instant.ofEpochMilli(2)); } - // this covers mapping of attributes in FocusSqlTransformer + GenericObject + // This covers mapping of attributes in QFocusMapping + GenericObject. @Test public void test820GenericObject() throws Exception { OperationResult result = createOperationResult(); @@ -869,6 +974,54 @@ public void test820GenericObject() throws Exception { assertCachedUri(row.genericObjectTypeId, "some-custom-object-type-uri"); } + // This covers mapping of attributes in AbstractRole + Archetype + inducement mapping. + // There is no focus on QFocusMapping that is covered above. + @Test + public void test821ArchetypeAndInducement() throws Exception { + OperationResult result = createOperationResult(); + + given("archetype object"); + String objectName = "arch" + getTestNumber(); + ArchetypeType archetype = new ArchetypeType(prismContext) + .name(objectName) + .autoassign(new AutoassignSpecificationType().enabled(true)) + .displayName("display-name") + .identifier("identifier") + .requestable(false) + .riskLevel("extremely-high") + // we don't need all attributes here, this is tested in TODO + .inducement(new AssignmentType() + .order(2) + .targetRef(UUID.randomUUID().toString(), RoleType.COMPLEX_TYPE)) + .inducement(new AssignmentType() + .order(3) + .targetRef(UUID.randomUUID().toString(), RoleType.COMPLEX_TYPE)); + // this is no additional attribute specific for archetype + + when("adding it to the repository"); + repositoryService.addObject(archetype.asPrismObject(), null, result); + + then("it is stored and relevant attributes are in columns"); + assertThatOperationResult(result).isSuccess(); + + UUID archetypeOid = UUID.fromString(archetype.getOid()); + MArchetype row = selectObjectByOid(QArchetype.class, archetypeOid); + // all attributes from MAbstractRole + assertThat(row.autoAssignEnabled).isTrue(); + assertThat(row.displayNameOrig).isEqualTo("display-name"); + assertThat(row.displayNameNorm).isEqualTo("displayname"); + assertThat(row.identifier).isEqualTo("identifier"); + assertThat(row.requestable).isFalse(); + assertThat(row.riskLevel).isEqualTo("extremely-high"); + + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); + assertThat(select(a, a.ownerOid.eq(archetypeOid))).hasSize(2) + .anyMatch(ar -> ar.orderValue.equals(2)) + .anyMatch(ar -> ar.orderValue.equals(3)) + .allMatch(ar -> ar.targetRefTargetOid != null + && ar.targetRefTargetType == MObjectType.ROLE); + } + // TODO test for focus' related entities? @Test diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java index 84b41cb7ae5..1674aadb182 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java @@ -859,7 +859,7 @@ public void test160AddingProjectionRefInsertsRowsToTable() assertThat(row.version).isEqualTo(originalRow.version + 1); and("externalized refs are inserted to the dedicated table"); - QObjectReference r = QObjectReferenceMapping.INSTANCE_PROJECTION.defaultAlias(); + QObjectReference r = QObjectReferenceMapping.getForProjection().defaultAlias(); UUID ownerOid = UUID.fromString(user1Oid); List refs = select(r, r.ownerOid.eq(ownerOid)); assertThat(refs).hasSize(1) @@ -903,7 +903,7 @@ public void test161AddingMoreProjectionRefsInsertsRowsToTable() assertThat(row.version).isEqualTo(originalRow.version + 1); and("externalized refs are inserted to the dedicated table"); - QObjectReference r = QObjectReferenceMapping.INSTANCE_PROJECTION.defaultAlias(); + QObjectReference r = QObjectReferenceMapping.getForProjection().defaultAlias(); List refs = select(r, r.ownerOid.eq(UUID.fromString(user1Oid))); assertThat(refs).hasSize(3) .anyMatch(refRowMatcher(refTargetOid, refRelation1)) @@ -946,7 +946,7 @@ public void test162ReplacingProjectionRefs() assertThat(row.version).isEqualTo(originalRow.version + 1); and("externalized refs are inserted to the dedicated table"); - QObjectReference r = QObjectReferenceMapping.INSTANCE_PROJECTION.defaultAlias(); + QObjectReference r = QObjectReferenceMapping.getForProjection().defaultAlias(); List refs = select(r, r.ownerOid.eq(UUID.fromString(user1Oid))); assertThat(refs).hasSize(2) // new added, previous three or so are gone .anyMatch(refRowMatcher(refTargetOid, refRelation1)) @@ -1025,7 +1025,7 @@ public void test163ReplacingProjectionRefs() assertThat(row.version).isEqualTo(originalRow.version + 1); and("externalized refs are inserted and deleted accordingly"); - QObjectReference r = QObjectReferenceMapping.INSTANCE_PROJECTION.defaultAlias(); + QObjectReference r = QObjectReferenceMapping.getForProjection().defaultAlias(); List refs = select(r, r.ownerOid.eq(UUID.fromString(user1Oid))); assertThat(refs).hasSize(6) .anyMatch(refRowMatcher(refTargetOid1, refRelation2)) @@ -1065,7 +1065,7 @@ public void test164DeletingAllProjectionRefsUsingReplace() assertThat(row.version).isEqualTo(originalRow.version + 1); and("externalized refs are inserted and deleted accordingly"); - QObjectReference r = QObjectReferenceMapping.INSTANCE_PROJECTION.defaultAlias(); + QObjectReference r = QObjectReferenceMapping.getForProjection().defaultAlias(); assertThat(count(r, r.ownerOid.eq(UUID.fromString(user1Oid)))).isZero(); } @@ -1107,7 +1107,7 @@ public void test170AddingCreateApproverRefsUnderMetadata() assertThat(row.version).isEqualTo(originalRow.version + 1); and("externalized refs are inserted to the dedicated table"); - QObjectReference r = QObjectReferenceMapping.INSTANCE_OBJECT_CREATE_APPROVER.defaultAlias(); + QObjectReference r = QObjectReferenceMapping.getForObjectCreateApprover().defaultAlias(); UUID ownerOid = UUID.fromString(user1Oid); List refs = select(r, r.ownerOid.eq(ownerOid)); assertThat(refs).hasSize(2) @@ -1143,9 +1143,9 @@ public void test171DeletingMetadataContainerRemovesContainedRefs() MUser row = selectObjectByOid(QUser.class, user1Oid); assertThat(row.version).isEqualTo(originalRow.version + 1); - QObjectReference r = QObjectReferenceMapping.INSTANCE_OBJECT_CREATE_APPROVER.defaultAlias(); + QObjectReference r = QObjectReferenceMapping.getForObjectCreateApprover().defaultAlias(); assertThat(count(r, r.ownerOid.eq(UUID.fromString(user1Oid)))).isZero(); - r = QObjectReferenceMapping.INSTANCE_OBJECT_MODIFY_APPROVER.defaultAlias(); + r = QObjectReferenceMapping.getForObjectModifyApprover().defaultAlias(); assertThat(count(r, r.ownerOid.eq(UUID.fromString(user1Oid)))).isZero(); } // endregion @@ -1661,7 +1661,7 @@ public void test300AddAssignmentStoresItAndGeneratesMissingId() MUser row = selectObjectByOid(QUser.class, user1Oid); assertThat(row.version).isEqualTo(originalRow.version + 1); - QAssignment a = QAssignmentMapping.INSTANCE.defaultAlias(); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); MAssignment aRow = selectOne(a, a.ownerOid.eq(UUID.fromString(user1Oid))); assertThat(aRow.cid).isEqualTo(originalRow.containerIdSeq); assertThat(aRow.containerType).isEqualTo(MContainerType.ASSIGNMENT); @@ -1676,7 +1676,7 @@ public void test301ReplaceItemUnderMultiValueAssignment() OperationResult result = createOperationResult(); given("delta replacing single-value item inside assignment for user 1"); - QAssignment a = QAssignmentMapping.INSTANCE.defaultAlias(); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); MAssignment origAssignmentRow = selectOne(a, a.ownerOid.eq(UUID.fromString(user1Oid))); assertThat(origAssignmentRow.orderValue).isNull(); // wasn't previously set ObjectDelta delta = prismContext.deltaFor(UserType.class) @@ -1755,7 +1755,7 @@ public void test302AddingMoreAssignmentsIncludingNestedContainersAndRefs() assertThat(row.version).isEqualTo(originalRow.version + 1); assertThat(row.containerIdSeq).isEqualTo(originalRow.containerIdSeq + 2); - QAssignment a = QAssignmentMapping.INSTANCE.defaultAlias(); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); List aRows = select(a, a.ownerOid.eq(UUID.fromString(user1Oid))); assertThat(aRows).hasSize(3) .anyMatch(aRow -> aRow.cid < originalRow.containerIdSeq) // previous one @@ -1767,7 +1767,7 @@ && cachedUriById(aRow.createChannelId).equals("create-channel")) && aRow.resourceRefTargetOid.equals(resourceOid)); QAssignmentReference ar = - QAssignmentReferenceMapping.INSTANCE_ASSIGNMENT_CREATE_APPROVER.defaultAlias(); + QAssignmentReferenceMapping.getForAssignmentCreateApprover().defaultAlias(); List refRows = select(ar, ar.ownerOid.eq(UUID.fromString(user1Oid)) .and(ar.assignmentCid.eq(row.containerIdSeq - 2))); assertThat(refRows).hasSize(2) @@ -1781,7 +1781,7 @@ public void test303ModificationsOnOneAssignmentDoesNotAffectOthers() MUser originalRow = selectObjectByOid(QUser.class, user1Oid); given("delta changing item inside single assignments for user 1"); - QAssignment a = QAssignmentMapping.INSTANCE.defaultAlias(); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); long assOrder49Cid = selectOne(a, a.ownerOid.eq(UUID.fromString(user1Oid)), a.orderValue.eq(49)).cid; ObjectDelta delta = prismContext.deltaFor(UserType.class) @@ -1825,7 +1825,7 @@ public void test304MultipleModificationsOfExistingAssignment() MUser originalRow = selectObjectByOid(QUser.class, user1Oid); given("delta changing multiple assignments for user 1"); - QAssignment a = QAssignmentMapping.INSTANCE.defaultAlias(); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); long assOrder48Cid = selectOne(a, a.ownerOid.eq(UUID.fromString(user1Oid)), a.orderValue.eq(48)).cid; long assOrder50Cid = selectOne(a, @@ -1876,12 +1876,51 @@ public void test304MultipleModificationsOfExistingAssignment() // Approver references were removed from the only assignment that had them. QAssignmentReference ar = - QAssignmentReferenceMapping.INSTANCE_ASSIGNMENT_CREATE_APPROVER.defaultAlias(); + QAssignmentReferenceMapping.getForAssignmentCreateApprover().defaultAlias(); assertThat(count(ar, ar.ownerOid.eq(UUID.fromString(user1Oid)))).isZero(); } @Test - public void test305AddingAssignmentWithNewPrefilledCid() + public void test305DeleteAssignmentByContent() + throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { + OperationResult result = createOperationResult(); + MUser originalRow = selectObjectByOid(QUser.class, user1Oid); + + given("delta deleting assignments without CID by equality for user 1"); + ObjectDelta delta = prismContext.deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT) + .delete(new AssignmentType(prismContext).order(50)) + .asObjectDelta(user1Oid); + + when("modifyObject is called"); + repositoryService.modifyObject(UserType.class, user1Oid, delta.getModifications(), result); + + then("operation is successful"); + assertThatOperationResult(result).isSuccess(); + + and("serialized form (fullObject) is updated and assignments with only order 50"); + UserType userObject = repositoryService.getObject(UserType.class, user1Oid, null, result) + .asObjectable(); + assertThat(userObject.getVersion()).isEqualTo(String.valueOf(originalRow.version + 1)); + List assignments = userObject.getAssignment(); + assertThat(assignments).hasSize(2) + .anyMatch(a -> a.getOrder().equals(47)) + // this one had order AND target ref, so it's no match + .anyMatch(ass -> ass.getOrder() == 50 && ass.getTargetRef() != null); + + and("corresponding assignment row is deleted"); + MUser row = selectObjectByOid(QUser.class, user1Oid); + assertThat(row.version).isEqualTo(originalRow.version + 1); + + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); + List aRows = select(a, a.ownerOid.eq(UUID.fromString(user1Oid))); + assertThat(aRows).hasSize(2) + .anyMatch(aRow -> aRow.orderValue.equals(47)) + .anyMatch(ass -> ass.orderValue == 50 && ass.targetRefTargetOid != null); + } + + @Test + public void test310AddingAssignmentWithNewPrefilledCid() throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { OperationResult result = createOperationResult(); MUser originalRow = selectObjectByOid(QUser.class, user1Oid); @@ -1905,7 +1944,7 @@ public void test305AddingAssignmentWithNewPrefilledCid() .asObjectable(); assertThat(userObject.getVersion()).isEqualTo(String.valueOf(originalRow.version + 1)); List assignments = userObject.getAssignment(); - assertThat(assignments).hasSize(4) + assertThat(assignments).hasSize(3) .anyMatch(a -> a.getId().equals(originalRow.containerIdSeq)); and("new assignment row is created"); @@ -1913,15 +1952,15 @@ public void test305AddingAssignmentWithNewPrefilledCid() assertThat(row.version).isEqualTo(originalRow.version + 1); assertThat(row.containerIdSeq).isEqualTo(originalRow.containerIdSeq + 1); - QAssignment a = QAssignmentMapping.INSTANCE.defaultAlias(); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); List aRows = select(a, a.ownerOid.eq(UUID.fromString(user1Oid))); - assertThat(aRows).hasSize(4) + assertThat(aRows).hasSize(3) .anyMatch(aRow -> aRow.cid.equals(originalRow.containerIdSeq) && aRow.orderValue == 1); } @Test - public void test306DeleteAssignmentByCid() + public void test311DeleteAssignmentByCid() throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { OperationResult result = createOperationResult(); MUser originalRow = selectObjectByOid(QUser.class, user1Oid); @@ -1944,7 +1983,7 @@ public void test306DeleteAssignmentByCid() .asObjectable(); assertThat(userObject.getVersion()).isEqualTo(String.valueOf(originalRow.version + 1)); List assignments = userObject.getAssignment(); - assertThat(assignments).hasSize(3) + assertThat(assignments).hasSize(2) .noneMatch(a -> a.getId().equals(originalRow.containerIdSeq - 1)); and("new assignment row is created"); @@ -1952,14 +1991,14 @@ public void test306DeleteAssignmentByCid() assertThat(row.version).isEqualTo(originalRow.version + 1); assertThat(row.containerIdSeq).isEqualTo(originalRow.containerIdSeq); // no need for change - QAssignment a = QAssignmentMapping.INSTANCE.defaultAlias(); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); List aRows = select(a, a.ownerOid.eq(UUID.fromString(user1Oid))); - assertThat(aRows).hasSize(3) + assertThat(aRows).hasSize(2) .noneMatch(aRow -> aRow.cid.equals(originalRow.containerIdSeq - 1)); } @Test - public void test307AddingAssignmentWithUsedBytFreeCid() + public void test312AddingAssignmentWithUsedBytFreeCid() throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { OperationResult result = createOperationResult(); MUser originalRow = selectObjectByOid(QUser.class, user1Oid); @@ -1984,7 +2023,7 @@ public void test307AddingAssignmentWithUsedBytFreeCid() .asObjectable(); assertThat(userObject.getVersion()).isEqualTo(String.valueOf(originalRow.version + 1)); List assignments = userObject.getAssignment(); - assertThat(assignments).hasSize(4) + assertThat(assignments).hasSize(3) .anyMatch(a -> a.getId().equals(originalRow.containerIdSeq - 1)); and("new assignment row is created"); @@ -1992,9 +2031,9 @@ public void test307AddingAssignmentWithUsedBytFreeCid() assertThat(row.version).isEqualTo(originalRow.version + 1); assertThat(row.containerIdSeq).isEqualTo(originalRow.containerIdSeq); // no change - QAssignment a = QAssignmentMapping.INSTANCE.defaultAlias(); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); List aRows = select(a, a.ownerOid.eq(UUID.fromString(user1Oid))); - assertThat(aRows).hasSize(4) + assertThat(aRows).hasSize(3) .anyMatch(aRow -> aRow.cid.equals(originalRow.containerIdSeq - 1) && aRow.orderValue == 1); } @@ -2002,7 +2041,7 @@ public void test307AddingAssignmentWithUsedBytFreeCid() // TODO delete by pattern - as per ItemImpl.remove(V, EquivalenceStrategy) @Test - public void test309DeleteAllAssignments() + public void test319DeleteAllAssignments() throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { OperationResult result = createOperationResult(); MUser originalRow = selectObjectByOid(QUser.class, user1Oid); @@ -2030,7 +2069,7 @@ public void test309DeleteAllAssignments() MUser row = selectObjectByOid(QUser.class, user1Oid); assertThat(row.version).isEqualTo(originalRow.version + 1); - QAssignment a = QAssignmentMapping.INSTANCE.defaultAlias(); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); assertThat(select(a, a.ownerOid.eq(UUID.fromString(user1Oid)))).isEmpty(); } // endregion diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditCleanupPerformanceTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditCleanupPerformanceTest.java index a12e6c0b926..1f4aef88e34 100644 --- a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditCleanupPerformanceTest.java +++ b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditCleanupPerformanceTest.java @@ -52,7 +52,7 @@ public void testAuditCleanup() throws Exception { then(); assertThat(result.isSuccess()).isTrue(); - assertThat(count(QAuditEventRecordMapping.INSTANCE)).isEqualTo(1); + assertThat(count(QAuditEventRecordMapping.get())).isEqualTo(1); } } @@ -72,7 +72,7 @@ private void prepareAuditEventRecords() throws Exception { } } - assertThat(count(QAuditEventRecordMapping.INSTANCE)).isEqualTo(RECORDS); + assertThat(count(QAuditEventRecordMapping.get())).isEqualTo(RECORDS); } private ObjectDeltaOperation createObjectDeltaOperation(int i) throws Exception { diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditTest.java index f207ca63f16..a9e33b37b51 100644 --- a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditTest.java +++ b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AuditTest.java @@ -8,8 +8,6 @@ import static org.assertj.core.api.Assertions.assertThat; -import com.evolveum.midpoint.schema.result.OperationResult; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; @@ -27,6 +25,7 @@ import com.evolveum.midpoint.repo.sql.util.RUtil; import com.evolveum.midpoint.repo.sqlbase.*; import com.evolveum.midpoint.schema.ObjectDeltaOperation; +import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.test.NullTaskImpl; import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordType; import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; @@ -153,10 +152,8 @@ private MAuditEventRecord getAuditEventRecord(int expectedCount, int index) throws QueryException { // "create" does not actually create a new audit service, but returns the existing one SqlRepoContext sqlRepoContext = auditServiceFactory.createAuditService().getSqlRepoContext(); - SqlTransformerSupport transformerSupport = new SqlTransformerSupport(schemaService, sqlRepoContext); SqlQueryContext context = - AuditSqlQueryContext.from( - AuditEventRecordType.class, transformerSupport, sqlRepoContext); + AuditSqlQueryContext.from(AuditEventRecordType.class, sqlRepoContext); QAuditEventRecord aer = context.root(); context.sqlQuery().orderBy(aer.id.asc()); diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/BaseSQLRepoTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/BaseSQLRepoTest.java index 488d48bbd60..1898d2735ac 100644 --- a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/BaseSQLRepoTest.java +++ b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/BaseSQLRepoTest.java @@ -363,7 +363,7 @@ protected , R> long count( /** Creates new {@link JdbcSession} based on {@link #baseHelper} setup. */ protected JdbcSession createJdbcSession() { - return new SqlRepoContext(baseHelper.getConfiguration(), baseHelper.dataSource(), null) + return new SqlRepoContext(baseHelper.getConfiguration(), baseHelper.dataSource(), schemaService, null) .newJdbcSession(); } } diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/CleanupTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/CleanupTest.java index 3a4ca692283..4b573fe4919 100644 --- a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/CleanupTest.java +++ b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/CleanupTest.java @@ -74,12 +74,12 @@ private CleanupPolicyType createPolicy(int maxRecords) { @AfterMethod public void cleanup() { try (JdbcSession jdbcSession = createJdbcSession().startTransaction()) { - jdbcSession.newDelete(QAuditDeltaMapping.INSTANCE.defaultAlias()).execute(); - jdbcSession.newDelete(QAuditItemMapping.INSTANCE.defaultAlias()).execute(); - jdbcSession.newDelete(QAuditPropertyValueMapping.INSTANCE.defaultAlias()).execute(); - jdbcSession.newDelete(QAuditResourceMapping.INSTANCE.defaultAlias()).execute(); - jdbcSession.newDelete(QAuditRefValueMapping.INSTANCE.defaultAlias()).execute(); - jdbcSession.newDelete(QAuditEventRecordMapping.INSTANCE.defaultAlias()).execute(); + jdbcSession.newDelete(QAuditDeltaMapping.get().defaultAlias()).execute(); + jdbcSession.newDelete(QAuditItemMapping.get().defaultAlias()).execute(); + jdbcSession.newDelete(QAuditPropertyValueMapping.get().defaultAlias()).execute(); + jdbcSession.newDelete(QAuditResourceMapping.get().defaultAlias()).execute(); + jdbcSession.newDelete(QAuditRefValueMapping.get().defaultAlias()).execute(); + jdbcSession.newDelete(QAuditEventRecordMapping.get().defaultAlias()).execute(); jdbcSession.commit(); } } @@ -168,7 +168,7 @@ private ObjectDeltaOperation createObjectDeltaOperation(int i) throws Excepti } private MAuditEventRecord assertAndReturnAuditEventRecord(int expectedCount) { - List records = select(QAuditEventRecordMapping.INSTANCE); + List records = select(QAuditEventRecordMapping.get()); AssertJUnit.assertEquals(expectedCount, records.size()); return records.get(0); } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/OracleConverter.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/OracleConverter.java deleted file mode 100644 index 712e743b148..00000000000 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/OracleConverter.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2010-2013 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.repo.sql; - -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Types; -/** - * @author skublik - */ -public class OracleConverter {//extends ConverterSqlAndJavaObject { - -// public T convertToValue(ResultSet rs, String nameOfColumn, Class javaClazz) throws SQLException { -// int index = rs.findColumn(nameOfColumn); -// return convertToValue(rs, index, javaClazz); -// } -// -// public T convertToValue(ResultSet rs, int index, Class javaClazz) throws SQLException { -// ResultSetMetaData metadata = rs.getMetaData(); -// int type = metadata.getColumnType(index); -//// if(javaClazz.equals(Long.class)) { -//// -//// } -// if(Types.) -// if(javaClazz.equals(String.class)) { -// return (T) rs.getString(nameOfColumn); -// } -// } - -// public Types getSqlType(Object value) { -// -// } -} diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceFactory.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceFactory.java index a77c277647d..e15f9d7499d 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceFactory.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceFactory.java @@ -27,10 +27,7 @@ import com.evolveum.midpoint.repo.sql.audit.mapping.*; import com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditEventRecord; import com.evolveum.midpoint.repo.sql.helpers.BaseHelper; -import com.evolveum.midpoint.repo.sqlbase.DataSourceFactory; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; -import com.evolveum.midpoint.repo.sqlbase.SqlTableMetadata; +import com.evolveum.midpoint.repo.sqlbase.*; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryModelMappingRegistry; import com.evolveum.midpoint.schema.SchemaService; import com.evolveum.midpoint.util.exception.SystemException; @@ -81,15 +78,6 @@ public synchronized void init(@NotNull Configuration configuration) throws Audit private SqlRepoContext createSqlRepoContext(Configuration configuration) throws RepositoryServiceFactoryException { - final QueryModelMappingRegistry auditModelMapping = new QueryModelMappingRegistry() - .register(AuditEventRecordType.COMPLEX_TYPE, QAuditEventRecordMapping.INSTANCE) - .register(QAuditItemMapping.INSTANCE) - .register(QAuditPropertyValueMapping.INSTANCE) - .register(QAuditRefValueMapping.INSTANCE) - .register(QAuditResourceMapping.INSTANCE) - .register(QAuditDeltaMapping.INSTANCE) - .seal(); - // one of these properties must be present to trigger separate audit datasource config if (configuration.getString(PROPERTY_JDBC_URL) == null && configuration.getString(PROPERTY_DATASOURCE) == null) { @@ -97,8 +85,10 @@ private SqlRepoContext createSqlRepoContext(Configuration configuration) // NOTE: If default BaseHelper is used, it's used to configure PerformanceMonitor // in SqlBaseService. Perhaps the base class is useless and these factories can provide // PerformanceMonitor for the services. - return new SqlRepoContext(defaultBaseHelper.getConfiguration(), - defaultBaseHelper.dataSource(), auditModelMapping); + return createSqlRepoContext( + defaultBaseHelper.getConfiguration(), + defaultBaseHelper.dataSource(), + schemaService); } LOGGER.info("Configuring SQL audit service to use a different datasource"); @@ -110,7 +100,28 @@ private SqlRepoContext createSqlRepoContext(Configuration configuration) DataSourceFactory dataSourceFactory = new DataSourceFactory(config); DataSource dataSource = dataSourceFactory.createDataSource(); - return new SqlRepoContext(config, dataSource, auditModelMapping); + return createSqlRepoContext(config, dataSource, schemaService); + } + + private SqlRepoContext createSqlRepoContext( + JdbcRepositoryConfiguration config, + DataSource dataSource, + SchemaService schemaService) { + QueryModelMappingRegistry mappingRegistry = new QueryModelMappingRegistry(); + SqlRepoContext repositoryContext = + new SqlRepoContext(config, dataSource, schemaService, mappingRegistry); + // Registered mapping needs repository context which needs registry - but we fill it now: + mappingRegistry + .register(AuditEventRecordType.COMPLEX_TYPE, + QAuditEventRecordMapping.init(repositoryContext)) + .register(QAuditItemMapping.init(repositoryContext)) + .register(QAuditPropertyValueMapping.init(repositoryContext)) + .register(QAuditRefValueMapping.init(repositoryContext)) + .register(QAuditResourceMapping.init(repositoryContext)) + .register(QAuditDeltaMapping.init(repositoryContext)) + .seal(); + + return repositoryContext; } private void initCustomColumns( @@ -142,7 +153,7 @@ private void initCustomColumns( ColumnMetadata columnMetadata = ColumnMetadata.named(columnName).ofType(Types.NVARCHAR).withSize(255); - QAuditEventRecordMapping.INSTANCE.addExtensionColumn(propertyName, columnMetadata); + QAuditEventRecordMapping.get().addExtensionColumn(propertyName, columnMetadata); if (tableMetadata != null && tableMetadata.get(columnName) == null) { // Fails on SQL Server with snapshot transaction, so different isolation is used. try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession() diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceImpl.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceImpl.java index 9c8984d9307..ad4a2ffc2e0 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceImpl.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceImpl.java @@ -102,9 +102,7 @@ public class SqlAuditServiceImpl extends SqlBaseService implements AuditService private final BaseHelper baseHelper; // only for logging/exception handling private final SqlRepoContext sqlRepoContext; private final SchemaService schemaService; - private final SqlQueryExecutor sqlQueryExecutor; - private final SqlTransformerSupport transformerSupport; private volatile SystemConfigurationAuditType auditConfiguration; @@ -116,7 +114,6 @@ public SqlAuditServiceImpl( this.sqlRepoContext = sqlRepoContext; this.schemaService = schemaService; this.sqlQueryExecutor = new SqlQueryExecutor(sqlRepoContext); - this.transformerSupport = new SqlTransformerSupport(schemaService, sqlRepoContext); } public SqlRepoContext getSqlRepoContext() { @@ -177,11 +174,9 @@ private void auditAttempt(AuditEventRecord record) { */ private Long insertAuditEventRecord( JdbcSession jdbcSession, AuditEventRecord record) { - QAuditEventRecordMapping aerMapping = QAuditEventRecordMapping.INSTANCE; + QAuditEventRecordMapping aerMapping = QAuditEventRecordMapping.get(); QAuditEventRecord aer = aerMapping.defaultAlias(); - MAuditEventRecord aerBean = aerMapping - .createTransformer(transformerSupport) - .from(record); + MAuditEventRecord aerBean = aerMapping.toRowObject(record); SQLInsertClause insert = jdbcSession.newInsert(aer).populate(aerBean); Map customColumns = aerMapping.getExtensionColumns(); @@ -213,7 +208,7 @@ private Collection insertAuditDeltas( if (!deltasByChecksum.isEmpty()) { SQLInsertClause insertBatch = jdbcSession.newInsert( - QAuditDeltaMapping.INSTANCE.defaultAlias()); + QAuditDeltaMapping.get().defaultAlias()); for (MAuditDelta value : deltasByChecksum.values()) { // NULLs are important to keep the value count consistent during the batch insertBatch.populate(value, DefaultMapper.WITH_NULL_BINDINGS).addBatch(); @@ -305,7 +300,7 @@ private void insertChangedItemPaths( } } if (!changedItemPaths.isEmpty()) { - QAuditItem qAuditItem = QAuditItemMapping.INSTANCE.defaultAlias(); + QAuditItem qAuditItem = QAuditItemMapping.get().defaultAlias(); SQLInsertClause insertBatch = jdbcSession.newInsert(qAuditItem); for (String changedItemPath : changedItemPaths) { insertBatch.set(qAuditItem.recordId, recordId) @@ -323,7 +318,7 @@ private void insertProperties( return; } - QAuditPropertyValue qAuditPropertyValue = QAuditPropertyValueMapping.INSTANCE.defaultAlias(); + QAuditPropertyValue qAuditPropertyValue = QAuditPropertyValueMapping.get().defaultAlias(); SQLInsertClause insertBatch = jdbcSession.newInsert(qAuditPropertyValue); for (String propertyName : properties.keySet()) { for (String propertyValue : properties.get(propertyName)) { @@ -348,7 +343,7 @@ private void insertReferences(JdbcSession jdbcSession, return; } - QAuditRefValue qAuditRefValue = QAuditRefValueMapping.INSTANCE.defaultAlias(); + QAuditRefValue qAuditRefValue = QAuditRefValueMapping.get().defaultAlias(); SQLInsertClause insertBatch = jdbcSession.newInsert(qAuditRefValue); for (String refName : references.keySet()) { for (AuditReferenceValue refValue : references.get(refName)) { @@ -377,7 +372,7 @@ private void insertResourceOids( return; } - QAuditResource qAuditResource = QAuditResourceMapping.INSTANCE.defaultAlias(); + QAuditResource qAuditResource = QAuditResourceMapping.get().defaultAlias(); SQLInsertClause insertBatch = jdbcSession.newInsert(qAuditResource); for (String resourceOid : resourceOids) { insertBatch.set(qAuditResource.recordId, recordId) @@ -501,7 +496,7 @@ private AuditEventRecord createAuditEventRecordAggregate( throws SQLException { AuditEventRecord audit = createAuditEventRecord(resultList); final Map customColumns = - QAuditEventRecordMapping.INSTANCE.getExtensionColumns(); + QAuditEventRecordMapping.get().getExtensionColumns(); for (Entry entry : customColumns.entrySet()) { audit.getCustomColumnProperty().put(entry.getKey(), resultList.getString(entry.getValue().getName())); @@ -979,7 +974,7 @@ private int batchDeletionAttempt( private int selectRecordsByMaxAge( JdbcSession jdbcSession, String tempTable, Date minValue) { - QAuditEventRecord aer = QAuditEventRecordMapping.INSTANCE.defaultAlias(); + QAuditEventRecord aer = QAuditEventRecordMapping.get().defaultAlias(); SQLQuery populateQuery = jdbcSession.newQuery() .select(aer.id) .from(aer) @@ -994,7 +989,7 @@ private int selectRecordsByMaxAge( private int selectRecordsByNumberToKeep( JdbcSession jdbcSession, String tempTable, int recordsToKeep) { - QAuditEventRecord aer = QAuditEventRecordMapping.INSTANCE.defaultAlias(); + QAuditEventRecord aer = QAuditEventRecordMapping.get().defaultAlias(); long totalAuditRecords = jdbcSession.newQuery().from(aer).fetchCount(); // we will find the number to delete and limit it to range [0,CLEANUP_AUDIT_BATCH_SIZE] @@ -1131,7 +1126,7 @@ public int countObjects( try { var queryContext = AuditSqlQueryContext.from( - AuditEventRecordType.class, transformerSupport, sqlRepoContext); + AuditEventRecordType.class, sqlRepoContext); return sqlQueryExecutor.count(queryContext, query, options); } catch (RepositoryException | RuntimeException e) { baseHelper.handleGeneralException(e, operationResult); @@ -1157,7 +1152,7 @@ public SearchResultList searchObjects( try { var queryContext = AuditSqlQueryContext.from( - AuditEventRecordType.class, transformerSupport, sqlRepoContext); + AuditEventRecordType.class, sqlRepoContext); SearchResultList result = sqlQueryExecutor.list(queryContext, query, options); return result; diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/AuditSqlQueryContext.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/AuditSqlQueryContext.java index 81068d051ed..516d6e65d6c 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/AuditSqlQueryContext.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/AuditSqlQueryContext.java @@ -10,9 +10,7 @@ import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; -import com.evolveum.midpoint.repo.sqlbase.mapping.SqlTransformer; import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; /** @@ -25,7 +23,7 @@ public class AuditSqlQueryContext, R> // Type parameters the same as in the class documentation. public static , R> AuditSqlQueryContext from( - Class schemaType, SqlTransformerSupport transformerSupport, SqlRepoContext sqlRepoContext) { + Class schemaType, SqlRepoContext sqlRepoContext) { QueryTableMapping rootMapping = sqlRepoContext.getMappingBySchemaType(schemaType); Q rootPath = rootMapping.defaultAlias(); @@ -35,27 +33,21 @@ public static , R> AuditSqlQueryConte query.getMetadata().setValidate(true); return new AuditSqlQueryContext<>( - rootPath, rootMapping, sqlRepoContext, transformerSupport, query); + rootPath, rootMapping, sqlRepoContext, query); } private AuditSqlQueryContext( Q entityPath, QueryTableMapping mapping, SqlRepoContext sqlRepoContext, - SqlTransformerSupport transformerSupport, SQLQuery query) { - super(entityPath, mapping, sqlRepoContext, transformerSupport, query); - } - - @Override - protected SqlTransformer createTransformer() { - return entityPathMapping.createTransformer(transformerSupport); + super(entityPath, mapping, sqlRepoContext, query); } @Override protected , TR> SqlQueryContext deriveNew(TQ newPath, QueryTableMapping newMapping) { return new AuditSqlQueryContext<>( - newPath, newMapping, sqlRepoContext, transformerSupport, sqlQuery); + newPath, newMapping, repositoryContext(), sqlQuery); } } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/beans/MAuditEventRecord.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/beans/MAuditEventRecord.java index 40560e8350f..0466a587903 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/beans/MAuditEventRecord.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/beans/MAuditEventRecord.java @@ -9,22 +9,9 @@ import java.time.Instant; import java.util.*; -import com.evolveum.midpoint.repo.sql.audit.mapping.AuditEventRecordSqlTransformer; import com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditEventRecord; -/** - * Querydsl "row bean" type related to {@link QAuditEventRecord}. - *

- * Design notes (TODO reconsider in 2021 and possibly remove if settled): - * This bean is super stupid for now. - * It can have getters/setters and handle more transformations and trimming (in setters), - * but at this moment this is left to transformation code like in {@link AuditEventRecordSqlTransformer}. - * I'd not suggest to move complete transformations here, but small conversions and trimming could - * be here - but how to do it reasonably without converting all fields to private + set/get methods? - * Can additional setter indicate conversion/trimming duties? - * What about methods like {@code SqlTransformerBase.trim()}, should we create common supertype - * to make it easy to call them in M-beans? Or should we use RUtil? - */ +/** Querydsl "row bean" type related to {@link QAuditEventRecord}. */ @SuppressWarnings("unused") public class MAuditEventRecord { diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/AuditDeltaSqlTransformer.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/AuditDeltaSqlTransformer.java deleted file mode 100644 index 7a820f582ba..00000000000 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/AuditDeltaSqlTransformer.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sql.audit.mapping; - -import com.querydsl.sql.SQLServerTemplates; -import com.querydsl.sql.SQLTemplates; - -import com.evolveum.midpoint.prism.polystring.PolyString; -import com.evolveum.midpoint.repo.sql.audit.AuditSqlTransformerBase; -import com.evolveum.midpoint.repo.sql.audit.beans.MAuditDelta; -import com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditDelta; -import com.evolveum.midpoint.repo.sql.util.RUtil; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectDeltaOperationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType; -import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; -import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; - -/** - * Transformer between repo and Prism world for audit event. - */ -public class AuditDeltaSqlTransformer - extends AuditSqlTransformerBase { - - private static final Trace LOGGER = TraceManager.getTrace(AuditDeltaSqlTransformer.class); - - public AuditDeltaSqlTransformer( - SqlTransformerSupport transformerSupport, QAuditDeltaMapping mapping) { - super(transformerSupport, mapping); - } - - public ObjectDeltaOperationType toSchemaObject(MAuditDelta row) { - ObjectDeltaOperationType odo = new ObjectDeltaOperationType(); - SQLTemplates querydslTemplates = transformerSupport.sqlRepoContext().getQuerydslTemplates(); - - boolean usingSqlServer = querydslTemplates instanceof SQLServerTemplates; - odo.setObjectDelta(parseBytes(row.delta, usingSqlServer, ObjectDeltaType.class)); - odo.setExecutionResult(parseBytes(row.fullResult, usingSqlServer, OperationResultType.class)); - - if (row.objectNameOrig != null || row.objectNameNorm != null) { - odo.setObjectName(new PolyStringType( - new PolyString(row.objectNameOrig, row.objectNameNorm))); - } - odo.setResourceOid(row.resourceOid); - if (row.resourceNameOrig != null || row.resourceNameNorm != null) { - odo.setResourceName(new PolyStringType( - new PolyString(row.resourceNameOrig, row.resourceNameNorm))); - } - - return odo; - } - - private T parseBytes(byte[] bytes, boolean usingSqlServer, Class clazz) { - if (bytes == null) { - return null; - } - - try { - return transformerSupport - .createStringParser(RUtil.getSerializedFormFromBytes(bytes, usingSqlServer)) - .compat() - .parseRealValue(clazz); - } catch (SchemaException e) { - LOGGER.error("Cannot parse {}: {}", clazz.getSimpleName(), e.getMessage(), e); - return null; - } - } -} diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/AuditEventRecordSqlTransformer.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/AuditEventRecordSqlTransformer.java deleted file mode 100644 index dbca9ac7ccd..00000000000 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/AuditEventRecordSqlTransformer.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sql.audit.mapping; - -import static com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditEventRecord.MESSAGE; - -import java.util.List; -import java.util.Map; -import javax.xml.namespace.QName; - -import com.querydsl.core.Tuple; - -import com.evolveum.midpoint.audit.api.AuditEventRecord; -import com.evolveum.midpoint.prism.PrismReferenceValue; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.prism.polystring.PolyString; -import com.evolveum.midpoint.repo.sql.audit.AuditSqlTransformerBase; -import com.evolveum.midpoint.repo.sql.audit.beans.MAuditDelta; -import com.evolveum.midpoint.repo.sql.audit.beans.MAuditEventRecord; -import com.evolveum.midpoint.repo.sql.audit.beans.MAuditRefValue; -import com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditDelta; -import com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditEventRecord; -import com.evolveum.midpoint.repo.sql.data.audit.RAuditEventStage; -import com.evolveum.midpoint.repo.sql.data.audit.RAuditEventType; -import com.evolveum.midpoint.repo.sql.data.common.enums.ROperationResultStatus; -import com.evolveum.midpoint.repo.sql.data.common.other.RObjectType; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.repo.sqlbase.mapping.SqlTransformer; -import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.xml.ns._public.common.audit_3.*; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectDeltaOperationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultStatusType; -import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; -import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; - -/** - * Transformation of audit event records between repo and Prism world. - */ -public class AuditEventRecordSqlTransformer - extends AuditSqlTransformerBase { - - public AuditEventRecordSqlTransformer( - SqlTransformerSupport transformerSupport, QAuditEventRecordMapping mapping) { - super(transformerSupport, mapping); - } - - public AuditEventRecordType toSchemaObject(MAuditEventRecord row) throws SchemaException { - AuditEventRecordType record = mapSimpleAttributes(row); - mapDeltas(record, row.deltas); - mapChangedItems(record, row.changedItemPaths); - mapRefValues(record, row.refValues); - mapProperties(record, row.properties); - mapResourceOids(record, row.resourceOids); - return record; - } - - private AuditEventRecordType mapSimpleAttributes(MAuditEventRecord row) { - // prismContext in constructor ensures complex type definition - return new AuditEventRecordType(transformerSupport.prismContext()) - .repoId(row.id) - .channel(row.channel) - .eventIdentifier(row.eventIdentifier) - .eventStage(auditEventStageTypeFromRepo(row.eventStage)) - .eventType(auditEventTypeTypeFromRepo(row.eventType)) - .hostIdentifier(row.hostIdentifier) - .message(row.message) - .nodeIdentifier(row.nodeIdentifier) - .outcome(operationResultStatusTypeFromRepo(row.outcome)) - .parameter(row.parameter) - .remoteHostAddress(row.remoteHostAddress) - .requestIdentifier(row.requestIdentifier) - .result(row.result) - .sessionIdentifier(row.sessionIdentifier) - .taskIdentifier(row.taskIdentifier) - .taskOID(row.taskOid) - .timestamp(MiscUtil.asXMLGregorianCalendar(row.timestamp)) - .initiatorRef(objectReferenceType( - row.initiatorOid, - repoObjectType(row.initiatorType, RObjectType.FOCUS), - row.initiatorName)) - .attorneyRef(objectReferenceType( - row.attorneyOid, RObjectType.FOCUS, row.attorneyName)) - .targetRef(objectReferenceType( - row.targetOid, - // targetType must not be null if targetOid is not - repoObjectType(row.targetType), - row.targetName)) - .targetOwnerRef(objectReferenceType( - row.targetOwnerOid, - repoObjectType(row.targetOwnerType), - row.targetOwnerName)); - } - - private void mapDeltas(AuditEventRecordType record, List deltas) - throws SchemaException { - if (deltas == null) { - return; - } - - SqlTransformer deltaTransformer = - QAuditDeltaMapping.INSTANCE.createTransformer(transformerSupport); - for (MAuditDelta delta : deltas) { - record.delta(deltaTransformer.toSchemaObject(delta)); - } - } - - // the rest of sub-entities do not deserve the dedicated transformer classes (yet) - private void mapChangedItems(AuditEventRecordType record, List changedItemPaths) { - if (changedItemPaths == null) { - return; - } - - for (String changedItemPath : changedItemPaths) { - ItemPath itemPath = ItemPath.create(changedItemPath); - record.getChangedItem().add(new ItemPathType(itemPath)); - } - } - - private void mapRefValues( - AuditEventRecordType record, Map> refValues) { - if (refValues == null) { - return; - } - - for (Map.Entry> entry : refValues.entrySet()) { - AuditEventRecordReferenceType referenceValues = - new AuditEventRecordReferenceType().name(entry.getKey()); - for (MAuditRefValue refValue : entry.getValue()) { - AuditEventRecordReferenceValueType value = new AuditEventRecordReferenceValueType() - .oid(refValue.oid) - .type(QName.valueOf(refValue.type)); - if (refValue.targetNameOrig != null) { - value.targetName(new PolyStringType( - new PolyString(refValue.targetNameOrig, refValue.targetNameNorm))); - } - referenceValues.value(value); - } - record.reference(referenceValues); - } - } - - private void mapProperties(AuditEventRecordType record, Map> properties) { - if (properties == null) { - return; - } - - for (Map.Entry> entry : properties.entrySet()) { - AuditEventRecordPropertyType propType = - new AuditEventRecordPropertyType().name(entry.getKey()); - propType.getValue().addAll(entry.getValue()); - record.property(propType); - } - } - - private void mapResourceOids( - AuditEventRecordType record, List resourceOids) { - if (resourceOids == null) { - return; - } - - record.getResourceOid().addAll(resourceOids); - } - - private AuditEventTypeType auditEventTypeTypeFromRepo(Integer ordinal) { - RAuditEventType eventType = MiscUtil.enumFromOrdinal(RAuditEventType.class, ordinal); - return eventType != null - ? eventType.getSchemaValue() - : null; - } - - private AuditEventStageType auditEventStageTypeFromRepo(Integer ordinal) { - RAuditEventStage stage = MiscUtil.enumFromOrdinal(RAuditEventStage.class, ordinal); - return stage != null - ? stage.getSchemaValue() - : null; - } - - private OperationResultStatusType operationResultStatusTypeFromRepo(Integer ordinal) { - ROperationResultStatus status = - MiscUtil.enumFromOrdinal(ROperationResultStatus.class, ordinal); - return status != null - ? status.getSchemaValue() - : null; - } - - /** - * Transforms {@link AuditEventRecord} to {@link MAuditEventRecord} without any subentities. - *

- * Design notes: Arguably, this code could be in {@link MAuditEventRecord}. - * Also the - */ - public MAuditEventRecord from(AuditEventRecord record) { - MAuditEventRecord bean = new MAuditEventRecord(); - bean.id = record.getRepoId(); // this better be null if we want to insert - bean.eventIdentifier = record.getEventIdentifier(); - bean.timestamp = MiscUtil.asInstant(record.getTimestamp()); - bean.channel = record.getChannel(); - bean.eventStage = MiscUtil.enumOrdinal(RAuditEventStage.from(record.getEventStage())); - bean.eventType = MiscUtil.enumOrdinal(RAuditEventType.from(record.getEventType())); - bean.hostIdentifier = record.getHostIdentifier(); - - PrismReferenceValue attorney = record.getAttorneyRef(); - if (attorney != null) { - bean.attorneyName = attorney.getDescription(); - bean.attorneyOid = attorney.getOid(); - } - - PrismReferenceValue initiator = record.getInitiatorRef(); - if (initiator != null) { - bean.initiatorName = initiator.getDescription(); - bean.initiatorOid = initiator.getOid(); - bean.initiatorType = targetTypeToRepoOrdinal(initiator); - } - - bean.message = trim(record.getMessage(), MESSAGE); - bean.nodeIdentifier = record.getNodeIdentifier(); - bean.outcome = MiscUtil.enumOrdinal(ROperationResultStatus.from(record.getOutcome())); - bean.parameter = record.getParameter(); - bean.remoteHostAddress = record.getRemoteHostAddress(); - bean.requestIdentifier = record.getRequestIdentifier(); - bean.result = record.getResult(); - bean.sessionIdentifier = record.getSessionIdentifier(); - - PrismReferenceValue target = record.getTargetRef(); - if (target != null) { - bean.targetName = target.getDescription(); - bean.targetOid = target.getOid(); - bean.targetType = targetTypeToRepoOrdinal(target); - } - PrismReferenceValue targetOwner = record.getTargetOwnerRef(); - if (targetOwner != null) { - bean.targetOwnerName = targetOwner.getDescription(); - bean.targetOwnerOid = targetOwner.getOid(); - bean.targetOwnerType = targetTypeToRepoOrdinal(targetOwner); - } - bean.taskIdentifier = record.getTaskIdentifier(); - bean.taskOid = record.getTaskOid(); - return bean; - } - - private Integer targetTypeToRepoOrdinal(PrismReferenceValue targetOwner) { - //noinspection rawtypes - Class objectClass = transformerSupport.qNameToSchemaClass(targetOwner.getTargetType()); - //noinspection unchecked - return MiscUtil.enumOrdinal(RObjectType.getByJaxbType(objectClass)); - } - - @Override - protected void processExtensionColumns( - AuditEventRecordType schemaObject, Tuple tuple, QAuditEventRecord entityPath) { - for (String propertyName : mapping.getExtensionColumns().keySet()) { - Object customColumnValue = tuple.get(entityPath.getPath(propertyName)); - schemaObject.getCustomColumnProperty().add( - new AuditEventRecordCustomColumnPropertyType() - .name(propertyName).value((String) customColumnValue)); - } - } -} diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/AuditSqlTransformerBase.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/AuditTableMapping.java similarity index 78% rename from repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/AuditSqlTransformerBase.java rename to repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/AuditTableMapping.java index 1deeef56da9..e1bbe198867 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/AuditSqlTransformerBase.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/AuditTableMapping.java @@ -4,7 +4,7 @@ * 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.repo.sql.audit; +package com.evolveum.midpoint.repo.sql.audit.mapping; import java.util.Collection; @@ -14,33 +14,32 @@ import org.jetbrains.annotations.Nullable; import com.evolveum.midpoint.repo.sql.data.common.other.RObjectType; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; -import com.evolveum.midpoint.repo.sqlbase.mapping.SqlTransformer; import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; -/** Transformation functionality common for audit. */ -public abstract class AuditSqlTransformerBase, R> - implements SqlTransformer { - - protected final SqlTransformerSupport transformerSupport; - protected final QueryTableMapping mapping; +/** + * Common functionality for audit table mapping, mostly around row/object transformation. + */ +public abstract class AuditTableMapping, R> + extends QueryTableMapping { - protected AuditSqlTransformerBase( - SqlTransformerSupport transformerSupport, QueryTableMapping mapping) { - this.transformerSupport = transformerSupport; - this.mapping = mapping; + protected AuditTableMapping( + @NotNull String tableName, + @NotNull String defaultAliasName, + @NotNull Class schemaType, + @NotNull Class queryType, + @NotNull SqlRepoContext repositoryContext) { + super(tableName, defaultAliasName, schemaType, queryType, repositoryContext); } @Override public S toSchemaObject( - Tuple tuple, Q entityPath, Collection> options) - throws SchemaException { + Tuple tuple, Q entityPath, Collection> options) { S schemaObject = toSchemaObject(tuple.get(entityPath)); processExtensionColumns(schemaObject, tuple, entityPath); return schemaObject; @@ -69,7 +68,7 @@ protected ObjectReferenceType objectReferenceType( return new ObjectReferenceType() .oid(oid) - .type(transformerSupport.schemaClassToQName(repoObjectType.getJaxbClass())) + .type(repositoryContext().schemaClassToQName(repoObjectType.getJaxbClass())) .description(targetName) .targetName(targetName); } @@ -109,4 +108,9 @@ protected ObjectReferenceType objectReferenceType( } return MiscUtil.trimString(value, columnMetadata.getSize()); } + + @Override + public S toSchemaObject(R row) { + throw new UnsupportedOperationException("Implemented in subclasses only"); + } } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditDeltaMapping.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditDeltaMapping.java index 29664f946cd..be10e3dedee 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditDeltaMapping.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditDeltaMapping.java @@ -8,26 +8,45 @@ import static com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditItem.TABLE_NAME; +import java.util.Objects; + +import com.querydsl.sql.SQLServerTemplates; +import com.querydsl.sql.SQLTemplates; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.repo.sql.audit.beans.MAuditDelta; import com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditDelta; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; -import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; -import com.evolveum.midpoint.repo.sqlbase.mapping.SqlTransformer; +import com.evolveum.midpoint.repo.sql.util.RUtil; +import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; +import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectDeltaOperationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType; +import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; +import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; /** * Mapping between {@link QAuditDelta} and {@link ObjectDeltaOperationType}. */ public class QAuditDeltaMapping - extends QueryTableMapping { + extends AuditTableMapping { public static final String DEFAULT_ALIAS_NAME = "ad"; - public static final QAuditDeltaMapping INSTANCE = new QAuditDeltaMapping(); + private static QAuditDeltaMapping instance; + + public static QAuditDeltaMapping init(@NotNull SqlRepoContext repositoryContext) { + instance = new QAuditDeltaMapping(repositoryContext); + return instance; + } - private QAuditDeltaMapping() { + public static QAuditDeltaMapping get() { + return Objects.requireNonNull(instance); + } + + private QAuditDeltaMapping(@NotNull SqlRepoContext repositoryContext) { super(TABLE_NAME, DEFAULT_ALIAS_NAME, - ObjectDeltaOperationType.class, QAuditDelta.class); + ObjectDeltaOperationType.class, QAuditDelta.class, repositoryContext); } @Override @@ -35,9 +54,40 @@ protected QAuditDelta newAliasInstance(String alias) { return new QAuditDelta(alias); } - @Override - public SqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new AuditDeltaSqlTransformer(transformerSupport, this); + public ObjectDeltaOperationType toSchemaObject(MAuditDelta row) { + ObjectDeltaOperationType odo = new ObjectDeltaOperationType(); + SQLTemplates querydslTemplates = repositoryContext().getQuerydslTemplates(); + + boolean usingSqlServer = querydslTemplates instanceof SQLServerTemplates; + odo.setObjectDelta(parseBytes(row.delta, usingSqlServer, ObjectDeltaType.class)); + odo.setExecutionResult(parseBytes(row.fullResult, usingSqlServer, OperationResultType.class)); + + if (row.objectNameOrig != null || row.objectNameNorm != null) { + odo.setObjectName(new PolyStringType( + new PolyString(row.objectNameOrig, row.objectNameNorm))); + } + odo.setResourceOid(row.resourceOid); + if (row.resourceNameOrig != null || row.resourceNameNorm != null) { + odo.setResourceName(new PolyStringType( + new PolyString(row.resourceNameOrig, row.resourceNameNorm))); + } + + return odo; + } + + private T parseBytes(byte[] bytes, boolean usingSqlServer, Class clazz) { + if (bytes == null) { + return null; + } + + try { + return repositoryContext() + .createStringParser(RUtil.getSerializedFormFromBytes(bytes, usingSqlServer)) + .compat() + .parseRealValue(clazz); + } catch (SchemaException e) { + logger.error("Cannot parse {}: {}", clazz.getSimpleName(), e.getMessage(), e); + return null; + } } } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditEventRecordMapping.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditEventRecordMapping.java index b764c2352a7..25c0d239264 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditEventRecordMapping.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditEventRecordMapping.java @@ -6,36 +6,64 @@ */ package com.evolveum.midpoint.repo.sql.audit.mapping; +import static com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditEventRecord.MESSAGE; import static com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditEventRecord.TABLE_NAME; import static com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordType.*; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import javax.xml.namespace.QName; + +import com.querydsl.core.Tuple; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.audit.api.AuditEventRecord; +import com.evolveum.midpoint.prism.PrismReferenceValue; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.polystring.PolyString; +import com.evolveum.midpoint.repo.sql.audit.beans.MAuditDelta; import com.evolveum.midpoint.repo.sql.audit.beans.MAuditEventRecord; +import com.evolveum.midpoint.repo.sql.audit.beans.MAuditRefValue; import com.evolveum.midpoint.repo.sql.audit.querymodel.*; import com.evolveum.midpoint.repo.sql.data.audit.RAuditEventStage; import com.evolveum.midpoint.repo.sql.data.audit.RAuditEventType; import com.evolveum.midpoint.repo.sql.data.common.enums.ROperationResultStatus; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sql.data.common.other.RObjectType; +import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; import com.evolveum.midpoint.repo.sqlbase.filtering.item.CanonicalItemPathItemFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.item.DetailTableItemFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.item.EnumOrdinalItemFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.item.SimpleItemFilterProcessor; -import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; import com.evolveum.midpoint.repo.sqlbase.mapping.SqlDetailFetchMapper; -import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordType; +import com.evolveum.midpoint.util.MiscUtil; +import com.evolveum.midpoint.xml.ns._public.common.audit_3.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultStatusType; +import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; +import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; /** * Mapping between {@link QAuditEventRecord} and {@link AuditEventRecordType}. */ public class QAuditEventRecordMapping - extends QueryTableMapping { + extends AuditTableMapping { public static final String DEFAULT_ALIAS_NAME = "aer"; - public static final QAuditEventRecordMapping INSTANCE = new QAuditEventRecordMapping(); + private static QAuditEventRecordMapping instance; + + public static QAuditEventRecordMapping init(@NotNull SqlRepoContext repositoryContext) { + instance = new QAuditEventRecordMapping(repositoryContext); + return instance; + } + + public static QAuditEventRecordMapping get() { + return Objects.requireNonNull(instance); + } - private QAuditEventRecordMapping() { + private QAuditEventRecordMapping(@NotNull SqlRepoContext repositoryContext) { super(TABLE_NAME, DEFAULT_ALIAS_NAME, - AuditEventRecordType.class, QAuditEventRecord.class); + AuditEventRecordType.class, QAuditEventRecord.class, repositoryContext); addItemMapping(F_REPO_ID, longMapper(q -> q.id)); addItemMapping(F_CHANNEL, stringMapper(q -> q.channel)); @@ -120,9 +148,208 @@ protected QAuditEventRecord newAliasInstance(String alias) { return new QAuditEventRecord(alias); } + public AuditEventRecordType toSchemaObject(MAuditEventRecord row) { + AuditEventRecordType record = mapSimpleAttributes(row); + mapDeltas(record, row.deltas); + mapChangedItems(record, row.changedItemPaths); + mapRefValues(record, row.refValues); + mapProperties(record, row.properties); + mapResourceOids(record, row.resourceOids); + return record; + } + + private AuditEventRecordType mapSimpleAttributes(MAuditEventRecord row) { + // prismContext in constructor ensures complex type definition + return new AuditEventRecordType(repositoryContext().prismContext()) + .repoId(row.id) + .channel(row.channel) + .eventIdentifier(row.eventIdentifier) + .eventStage(auditEventStageTypeFromRepo(row.eventStage)) + .eventType(auditEventTypeTypeFromRepo(row.eventType)) + .hostIdentifier(row.hostIdentifier) + .message(row.message) + .nodeIdentifier(row.nodeIdentifier) + .outcome(operationResultStatusTypeFromRepo(row.outcome)) + .parameter(row.parameter) + .remoteHostAddress(row.remoteHostAddress) + .requestIdentifier(row.requestIdentifier) + .result(row.result) + .sessionIdentifier(row.sessionIdentifier) + .taskIdentifier(row.taskIdentifier) + .taskOID(row.taskOid) + .timestamp(MiscUtil.asXMLGregorianCalendar(row.timestamp)) + .initiatorRef(objectReferenceType( + row.initiatorOid, + repoObjectType(row.initiatorType, RObjectType.FOCUS), + row.initiatorName)) + .attorneyRef(objectReferenceType( + row.attorneyOid, RObjectType.FOCUS, row.attorneyName)) + .targetRef(objectReferenceType( + row.targetOid, + // targetType must not be null if targetOid is not + repoObjectType(row.targetType), + row.targetName)) + .targetOwnerRef(objectReferenceType( + row.targetOwnerOid, + repoObjectType(row.targetOwnerType), + row.targetOwnerName)); + } + + private void mapDeltas(AuditEventRecordType record, List deltas) { + if (deltas == null) { + return; + } + + for (MAuditDelta delta : deltas) { + record.delta(QAuditDeltaMapping.get().toSchemaObject(delta)); + } + } + + private void mapChangedItems(AuditEventRecordType record, List changedItemPaths) { + if (changedItemPaths == null) { + return; + } + + for (String changedItemPath : changedItemPaths) { + ItemPath itemPath = ItemPath.create(changedItemPath); + record.getChangedItem().add(new ItemPathType(itemPath)); + } + } + + private void mapRefValues( + AuditEventRecordType record, Map> refValues) { + if (refValues == null) { + return; + } + + for (Map.Entry> entry : refValues.entrySet()) { + AuditEventRecordReferenceType referenceValues = + new AuditEventRecordReferenceType().name(entry.getKey()); + for (MAuditRefValue refValue : entry.getValue()) { + AuditEventRecordReferenceValueType value = new AuditEventRecordReferenceValueType() + .oid(refValue.oid) + .type(QName.valueOf(refValue.type)); + if (refValue.targetNameOrig != null) { + value.targetName(new PolyStringType( + new PolyString(refValue.targetNameOrig, refValue.targetNameNorm))); + } + referenceValues.value(value); + } + record.reference(referenceValues); + } + } + + private void mapProperties(AuditEventRecordType record, Map> properties) { + if (properties == null) { + return; + } + + for (Map.Entry> entry : properties.entrySet()) { + AuditEventRecordPropertyType propType = + new AuditEventRecordPropertyType().name(entry.getKey()); + propType.getValue().addAll(entry.getValue()); + record.property(propType); + } + } + + private void mapResourceOids( + AuditEventRecordType record, List resourceOids) { + if (resourceOids == null) { + return; + } + + record.getResourceOid().addAll(resourceOids); + } + + private AuditEventTypeType auditEventTypeTypeFromRepo(Integer ordinal) { + RAuditEventType eventType = MiscUtil.enumFromOrdinal(RAuditEventType.class, ordinal); + return eventType != null + ? eventType.getSchemaValue() + : null; + } + + private AuditEventStageType auditEventStageTypeFromRepo(Integer ordinal) { + RAuditEventStage stage = MiscUtil.enumFromOrdinal(RAuditEventStage.class, ordinal); + return stage != null + ? stage.getSchemaValue() + : null; + } + + private OperationResultStatusType operationResultStatusTypeFromRepo(Integer ordinal) { + ROperationResultStatus status = + MiscUtil.enumFromOrdinal(ROperationResultStatus.class, ordinal); + return status != null + ? status.getSchemaValue() + : null; + } + + /** + * Transforms {@link AuditEventRecord} to {@link MAuditEventRecord} without any subentities. + */ + public MAuditEventRecord toRowObject(AuditEventRecord record) { + MAuditEventRecord bean = new MAuditEventRecord(); + bean.id = record.getRepoId(); // this better be null if we want to insert + bean.eventIdentifier = record.getEventIdentifier(); + bean.timestamp = MiscUtil.asInstant(record.getTimestamp()); + bean.channel = record.getChannel(); + bean.eventStage = MiscUtil.enumOrdinal(RAuditEventStage.from(record.getEventStage())); + bean.eventType = MiscUtil.enumOrdinal(RAuditEventType.from(record.getEventType())); + bean.hostIdentifier = record.getHostIdentifier(); + + PrismReferenceValue attorney = record.getAttorneyRef(); + if (attorney != null) { + bean.attorneyName = attorney.getDescription(); + bean.attorneyOid = attorney.getOid(); + } + + PrismReferenceValue initiator = record.getInitiatorRef(); + if (initiator != null) { + bean.initiatorName = initiator.getDescription(); + bean.initiatorOid = initiator.getOid(); + bean.initiatorType = targetTypeToRepoOrdinal(initiator); + } + + bean.message = trim(record.getMessage(), MESSAGE); + bean.nodeIdentifier = record.getNodeIdentifier(); + bean.outcome = MiscUtil.enumOrdinal(ROperationResultStatus.from(record.getOutcome())); + bean.parameter = record.getParameter(); + bean.remoteHostAddress = record.getRemoteHostAddress(); + bean.requestIdentifier = record.getRequestIdentifier(); + bean.result = record.getResult(); + bean.sessionIdentifier = record.getSessionIdentifier(); + + PrismReferenceValue target = record.getTargetRef(); + if (target != null) { + bean.targetName = target.getDescription(); + bean.targetOid = target.getOid(); + bean.targetType = targetTypeToRepoOrdinal(target); + } + PrismReferenceValue targetOwner = record.getTargetOwnerRef(); + if (targetOwner != null) { + bean.targetOwnerName = targetOwner.getDescription(); + bean.targetOwnerOid = targetOwner.getOid(); + bean.targetOwnerType = targetTypeToRepoOrdinal(targetOwner); + } + bean.taskIdentifier = record.getTaskIdentifier(); + bean.taskOid = record.getTaskOid(); + return bean; + } + + private Integer targetTypeToRepoOrdinal(PrismReferenceValue targetOwner) { + //noinspection rawtypes + Class objectClass = repositoryContext().qNameToSchemaClass(targetOwner.getTargetType()); + //noinspection unchecked + return MiscUtil.enumOrdinal(RObjectType.getByJaxbType(objectClass)); + } + @Override - public AuditEventRecordSqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - return new AuditEventRecordSqlTransformer(transformerSupport, this); + protected void processExtensionColumns( + AuditEventRecordType schemaObject, Tuple tuple, QAuditEventRecord entityPath) { + for (String propertyName : getExtensionColumns().keySet()) { + Object customColumnValue = tuple.get(entityPath.getPath(propertyName)); + schemaObject.getCustomColumnProperty().add( + new AuditEventRecordCustomColumnPropertyType() + .name(propertyName).value((String) customColumnValue)); + } } } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditItemMapping.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditItemMapping.java index a615273c1b8..8e272ef8e16 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditItemMapping.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditItemMapping.java @@ -8,24 +8,37 @@ import static com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditItem.TABLE_NAME; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + import com.evolveum.midpoint.repo.sql.audit.beans.MAuditItem; import com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditItem; -import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; +import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; /** * Mapping for {@link QAuditItem}, model type is non-containerable {@link ItemPathType}. */ public class QAuditItemMapping - extends QueryTableMapping { + extends AuditTableMapping { public static final String DEFAULT_ALIAS_NAME = "ai"; - public static final QAuditItemMapping INSTANCE = new QAuditItemMapping(); + private static QAuditItemMapping instance; + + public static QAuditItemMapping init(@NotNull SqlRepoContext repositoryContext) { + instance = new QAuditItemMapping(repositoryContext); + return instance; + } + + public static QAuditItemMapping get() { + return Objects.requireNonNull(instance); + } - private QAuditItemMapping() { + private QAuditItemMapping(@NotNull SqlRepoContext repositoryContext) { super(TABLE_NAME, DEFAULT_ALIAS_NAME, - ItemPathType.class, QAuditItem.class); + ItemPathType.class, QAuditItem.class, repositoryContext); } @Override diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditPropertyValueMapping.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditPropertyValueMapping.java index 328c6cf5541..974a315f1cf 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditPropertyValueMapping.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditPropertyValueMapping.java @@ -8,24 +8,37 @@ import static com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditPropertyValue.TABLE_NAME; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + import com.evolveum.midpoint.repo.sql.audit.beans.MAuditPropertyValue; import com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditPropertyValue; -import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; +import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordPropertyType; /** * Mapping for {@link QAuditPropertyValue}. */ public class QAuditPropertyValueMapping - extends QueryTableMapping { + extends AuditTableMapping { public static final String DEFAULT_ALIAS_NAME = "apv"; - public static final QAuditPropertyValueMapping INSTANCE = new QAuditPropertyValueMapping(); + private static QAuditPropertyValueMapping instance; + + public static QAuditPropertyValueMapping init(@NotNull SqlRepoContext repositoryContext) { + instance = new QAuditPropertyValueMapping(repositoryContext); + return instance; + } + + public static QAuditPropertyValueMapping get() { + return Objects.requireNonNull(instance); + } - private QAuditPropertyValueMapping() { + private QAuditPropertyValueMapping(@NotNull SqlRepoContext repositoryContext) { super(TABLE_NAME, DEFAULT_ALIAS_NAME, - AuditEventRecordPropertyType.class, QAuditPropertyValue.class); + AuditEventRecordPropertyType.class, QAuditPropertyValue.class, repositoryContext); } @Override diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditRefValueMapping.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditRefValueMapping.java index 6507e3eeebd..5a0d50035e2 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditRefValueMapping.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditRefValueMapping.java @@ -8,24 +8,37 @@ import static com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditItem.TABLE_NAME; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + import com.evolveum.midpoint.repo.sql.audit.beans.MAuditRefValue; import com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditRefValue; -import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; +import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordReferenceType; /** * Mapping between {@link QAuditRefValue} and {@link AuditEventRecordReferenceType}. */ public class QAuditRefValueMapping - extends QueryTableMapping { + extends AuditTableMapping { public static final String DEFAULT_ALIAS_NAME = "aref"; - public static final QAuditRefValueMapping INSTANCE = new QAuditRefValueMapping(); + private static QAuditRefValueMapping instance; + + public static QAuditRefValueMapping init(@NotNull SqlRepoContext repositoryContext) { + instance = new QAuditRefValueMapping(repositoryContext); + return instance; + } + + public static QAuditRefValueMapping get() { + return Objects.requireNonNull(instance); + } - private QAuditRefValueMapping() { + private QAuditRefValueMapping(@NotNull SqlRepoContext repositoryContext) { super(TABLE_NAME, DEFAULT_ALIAS_NAME, - AuditEventRecordReferenceType.class, QAuditRefValue.class); + AuditEventRecordReferenceType.class, QAuditRefValue.class, repositoryContext); } @Override diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditResourceMapping.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditResourceMapping.java index 8f65c518dab..dc6f0faf1a1 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditResourceMapping.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/mapping/QAuditResourceMapping.java @@ -8,23 +8,36 @@ import static com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditItem.TABLE_NAME; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + import com.evolveum.midpoint.repo.sql.audit.beans.MAuditResource; import com.evolveum.midpoint.repo.sql.audit.querymodel.QAuditResource; -import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; +import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; /** - * Mapping for {@link QAuditResource}, no transformation supported. + * Mapping for {@link QAuditResource}. */ public class QAuditResourceMapping - extends QueryTableMapping { + extends AuditTableMapping { public static final String DEFAULT_ALIAS_NAME = "ares"; - public static final QAuditResourceMapping INSTANCE = new QAuditResourceMapping(); + private static QAuditResourceMapping instance; + + public static QAuditResourceMapping init(@NotNull SqlRepoContext repositoryContext) { + instance = new QAuditResourceMapping(repositoryContext); + return instance; + } + + public static QAuditResourceMapping get() { + return Objects.requireNonNull(instance); + } - private QAuditResourceMapping() { + private QAuditResourceMapping(@NotNull SqlRepoContext repositoryContext) { super(TABLE_NAME, DEFAULT_ALIAS_NAME, - String.class, QAuditResource.class); + String.class, QAuditResource.class, repositoryContext); } @Override diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/RepositoryObjectParseResult.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/RepositoryObjectParseResult.java new file mode 100644 index 00000000000..6c6fc5eaa5c --- /dev/null +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/RepositoryObjectParseResult.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010-2021 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.repo.sqlbase; + +import com.evolveum.midpoint.prism.Objectable; +import com.evolveum.midpoint.prism.ParsingContext; +import com.evolveum.midpoint.prism.PrismObject; + +/** Result for deserialization of prism object stored in the repository. */ +public class RepositoryObjectParseResult { + + public final ParsingContext parsingContext; + public final PrismObject prismObject; + + public RepositoryObjectParseResult(ParsingContext parsingContext, PrismObject prismObject) { + this.parsingContext = parsingContext; + this.prismObject = prismObject; + } +} diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java index 5bad518b18e..eeda9965227 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java @@ -28,8 +28,8 @@ import com.evolveum.midpoint.repo.sqlbase.filtering.FilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.ObjectFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; +import com.evolveum.midpoint.repo.sqlbase.mapping.RepositoryMappingException; import com.evolveum.midpoint.repo.sqlbase.mapping.SqlDetailFetchMapper; -import com.evolveum.midpoint.repo.sqlbase.mapping.SqlTransformer; import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; @@ -73,24 +73,21 @@ public abstract class SqlQueryContext protected final Q entityPath; protected final QueryTableMapping entityPathMapping; - protected final SqlRepoContext sqlRepoContext; - protected final SqlTransformerSupport transformerSupport; + private final SqlRepoContext sqlRepoContext; protected boolean notFilterUsed = false; - // options stored to modify select clause and also to affect transformation + // options stored to modify select clause and also to affect mapping protected Collection> options; protected SqlQueryContext( Q entityPath, QueryTableMapping mapping, SqlRepoContext sqlRepoContext, - SqlTransformerSupport transformerSupport, SQLQuery query) { this.entityPath = entityPath; this.entityPathMapping = mapping; this.sqlRepoContext = sqlRepoContext; - this.transformerSupport = transformerSupport; this.sqlQuery = query; } @@ -141,7 +138,8 @@ private void processOrdering(List orderings) throw new QueryException( "ORDER BY is not possible for complex paths: " + orderByItemPath); } - Path path = mapping().primarySqlPath(orderByItemPath.asSingleNameOrFail(), this); + Path path = entityPathMapping.primarySqlPath( + orderByItemPath.asSingleNameOrFail(), this); if (!(path instanceof ComparableExpressionBase)) { throw new QueryException( "ORDER BY is not possible for non-comparable path: " + orderByItemPath); @@ -172,12 +170,14 @@ public PageOf executeQuery(Connection conn) throws QueryException { .fetch(); // TODO: run fetchers selectively based on options? - if (!mapping().detailFetchMappers().isEmpty()) { + Collection> detailFetchMappers = + entityPathMapping.detailFetchMappers(); + if (!detailFetchMappers.isEmpty()) { // we don't want to extract R if no mappers exist, otherwise we want to do it only once List dataEntities = data.stream() .map(t -> t.get(entity)) .collect(Collectors.toList()); - for (SqlDetailFetchMapper fetcher : mapping().detailFetchMappers()) { + for (SqlDetailFetchMapper fetcher : detailFetchMappers) { fetcher.execute(sqlRepoContext, () -> sqlRepoContext.newQuery(conn), dataEntities); } } @@ -186,7 +186,7 @@ public PageOf executeQuery(Connection conn) throws QueryException { } private @NotNull Expression[] buildSelectExpressions(Q entity, SQLQuery query) { - Path[] defaultExpressions = mapping().selectExpressions(entity, options); + Path[] defaultExpressions = entityPathMapping.selectExpressions(entity, options); if (!query.getMetadata().isDistinct() || query.getMetadata().getOrderBy().isEmpty()) { return defaultExpressions; } @@ -293,9 +293,8 @@ public void processOptions(Collection> opti public PageOf transformToSchemaType(PageOf result) throws SchemaException, QueryException { try { - SqlTransformer transformer = createTransformer(); - return result.map(row -> transformer.toSchemaObjectSafe(row, root(), options)); - } catch (SqlTransformer.SqlTransformationException e) { + return result.map(row -> entityPathMapping.toSchemaObjectSafe(row, root(), options)); + } catch (RepositoryMappingException e) { Throwable cause = e.getCause(); if (cause instanceof SchemaException) { throw (SchemaException) cause; @@ -307,13 +306,6 @@ public PageOf transformToSchemaType(PageOf result) } } - /** - * Creates transformer for the {@link #entityPathMapping}. - * Made abstract, because the way how to create the transformer can differ on the type - * of context that is needed to do it (typically providing various components). - */ - protected abstract SqlTransformer createTransformer(); - /** * Returns wrapped query if usage of Querydsl API is more convenient. */ @@ -344,25 +336,25 @@ public boolean isNotFilterUsed() { return notFilterUsed; } - public SqlRepoContext sqlRepoContext() { + public SqlRepoContext repositoryContext() { return sqlRepoContext; } public PrismContext prismContext() { - return transformerSupport.prismContext(); + return sqlRepoContext.prismContext(); } public Class qNameToSchemaClass(@NotNull QName qName) { - return transformerSupport.qNameToSchemaClass(qName); + return sqlRepoContext.qNameToSchemaClass(qName); } public CanonicalItemPath createCanonicalItemPath(@NotNull ItemPath itemPath) { - return transformerSupport.prismContext().createCanonicalItemPath(itemPath); + return sqlRepoContext.prismContext().createCanonicalItemPath(itemPath); } @NotNull public QName normalizeRelation(QName qName) { - return transformerSupport.normalizeRelation(qName); + return sqlRepoContext.normalizeRelation(qName); } public FilterProcessor createInOidFilter(SqlQueryContext context) { diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryExecutor.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryExecutor.java index 63cb7773667..4476d443838 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryExecutor.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryExecutor.java @@ -68,8 +68,7 @@ public , R> SearchResultList list( result = context.executeQuery(jdbcSession.connection()); } - PageOf map = context.transformToSchemaType(result); - return createSearchResultList(map); + return createSearchResultList(context.transformToSchemaType(result)); } @NotNull diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlRepoContext.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlRepoContext.java index 518d015aadb..1936b8b1823 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlRepoContext.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlRepoContext.java @@ -9,6 +9,7 @@ import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; +import javax.xml.namespace.QName; import com.querydsl.sql.Configuration; import com.querydsl.sql.RelationalPath; @@ -17,11 +18,16 @@ import com.querydsl.sql.dml.SQLDeleteClause; import com.querydsl.sql.dml.SQLInsertClause; import com.querydsl.sql.dml.SQLUpdateClause; +import org.jetbrains.annotations.NotNull; +import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryModelMappingRegistry; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; import com.evolveum.midpoint.repo.sqlbase.querydsl.QuerydslUtils; +import com.evolveum.midpoint.schema.SchemaService; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SystemException; /** @@ -33,16 +39,19 @@ public class SqlRepoContext { private final JdbcRepositoryConfiguration jdbcRepositoryConfiguration; protected final Configuration querydslConfig; + protected final SchemaService schemaService; private final QueryModelMappingRegistry mappingRegistry; private final DataSource dataSource; public SqlRepoContext( JdbcRepositoryConfiguration jdbcRepositoryConfiguration, DataSource dataSource, + SchemaService schemaService, QueryModelMappingRegistry mappingRegistry) { this.jdbcRepositoryConfiguration = jdbcRepositoryConfiguration; this.querydslConfig = QuerydslUtils.querydslConfiguration( jdbcRepositoryConfiguration.getDatabaseType()); + this.schemaService = schemaService; this.mappingRegistry = mappingRegistry; this.dataSource = dataSource; } @@ -105,4 +114,50 @@ public JdbcSession newJdbcSession() { throw new SystemException("Cannot create JDBC connection", e); } } + + // TODO review from here - this is stuff merged from support service + public Class qNameToSchemaClass(QName qName) { + return schemaService.typeQNameToSchemaClass(qName); + } + + public QName schemaClassToQName(Class schemaClass) { + return schemaService.schemaClassToTypeQName(schemaClass); + } + + public @NotNull QName normalizeRelation(QName qName) { + return schemaService.normalizeRelation(qName); + } + + @NotNull + public PrismSerializer createStringSerializer() { + return schemaService.createStringSerializer( + getJdbcRepositoryConfiguration().getFullObjectFormat()); + } + + public RepositoryObjectParseResult parsePrismObject(String serializedForm) + throws SchemaException { + PrismContext prismContext = schemaService.prismContext(); + // "Postel mode": be tolerant what you read. We need this to tolerate (custom) schema changes + ParsingContext parsingContext = prismContext.createParsingContextForCompatibilityMode(); + PrismObject prismObject = prismContext.parserFor(serializedForm) + .context(parsingContext).parse(); + return new RepositoryObjectParseResult<>(parsingContext, prismObject); + } + + @NotNull + public PrismParserNoIO createStringParser(String serializedResult) { + return schemaService.parserFor(serializedResult); + } + + /** + * Sometimes delegation is not enough - we need Prism context for schema type construction + * with definitions (parameter to constructor). + */ + public PrismContext prismContext() { + return schemaService.prismContext(); + } + + public void normalizeAllRelations(PrismObject prismObject) { + ObjectTypeUtil.normalizeAllRelations(prismObject, schemaService.relationRegistry()); + } } diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlTransformerSupport.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlTransformerSupport.java deleted file mode 100644 index b007a0575de..00000000000 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlTransformerSupport.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqlbase; - -import javax.xml.namespace.QName; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.schema.SchemaService; -import com.evolveum.midpoint.schema.util.ObjectTypeUtil; -import com.evolveum.midpoint.util.exception.SchemaException; - -/** - * Holds various component dependencies that are used during schema to DB transformations. - * Components can be obtained to execute calls on them, but preferably the needed logic - * can be implemented here (better abstraction). - */ -public class SqlTransformerSupport { - - protected final SchemaService schemaService; - protected final SqlRepoContext sqlRepoContext; - - public SqlTransformerSupport(SchemaService schemaService, SqlRepoContext sqlRepoContext) { - this.schemaService = schemaService; - this.sqlRepoContext = sqlRepoContext; - } - - public Class qNameToSchemaClass(QName qName) { - return schemaService.typeQNameToSchemaClass(qName); - } - - public QName schemaClassToQName(Class schemaClass) { - return schemaService.schemaClassToTypeQName(schemaClass); - } - - public @NotNull QName normalizeRelation(QName qName) { - return schemaService.normalizeRelation(qName); - } - - @NotNull - public PrismSerializer createStringSerializer() { - return schemaService.createStringSerializer( - sqlRepoContext.getJdbcRepositoryConfiguration().getFullObjectFormat()); - } - - public ParseResult parsePrismObject(String serializedForm) - throws SchemaException { - PrismContext prismContext = schemaService.prismContext(); - // "Postel mode": be tolerant what you read. We need this to tolerate (custom) schema changes - ParsingContext parsingContext = prismContext.createParsingContextForCompatibilityMode(); - PrismObject prismObject = prismContext.parserFor(serializedForm) - .context(parsingContext).parse(); - return new ParseResult<>(parsingContext, prismObject); - } - - @NotNull - public PrismParserNoIO createStringParser(String serializedResult) { - return schemaService.parserFor(serializedResult); - } - - /** - * Sometimes delegation is not enough - we need Prism context for schema type construction - * with definitions (parameter to constructor). - */ - public PrismContext prismContext() { - return schemaService.prismContext(); - } - - public SqlRepoContext sqlRepoContext() { - return sqlRepoContext; - } - - public void normalizeAllRelations(PrismObject prismObject) { - ObjectTypeUtil.normalizeAllRelations(prismObject, schemaService.relationRegistry()); - } - - public static class ParseResult { - public final ParsingContext parsingContext; - public final PrismObject prismObject; - - public ParseResult(ParsingContext parsingContext, PrismObject prismObject) { - this.parsingContext = parsingContext; - this.prismObject = prismObject; - } - } -} diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryModelMapping.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryModelMapping.java index edaa59726b5..73033d3b514 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryModelMapping.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryModelMapping.java @@ -13,6 +13,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.evolveum.midpoint.prism.path.ItemName; import com.evolveum.midpoint.prism.path.ItemPath; @@ -39,6 +41,8 @@ */ public class QueryModelMapping, R> { + protected final Logger logger = LoggerFactory.getLogger(getClass()); + private final Class schemaType; private final Class queryType; diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryTableMapping.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryTableMapping.java index e6da4e258af..a8d69609b3f 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryTableMapping.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryTableMapping.java @@ -11,6 +11,7 @@ import java.util.function.Function; import javax.xml.namespace.QName; +import com.querydsl.core.Tuple; import com.querydsl.core.types.EntityPath; import com.querydsl.core.types.Path; import com.querydsl.core.types.Predicate; @@ -26,7 +27,7 @@ import com.evolveum.midpoint.repo.sqlbase.QueryException; import com.evolveum.midpoint.repo.sqlbase.RepositoryException; import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; -import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; +import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; import com.evolveum.midpoint.repo.sqlbase.filtering.item.PolyStringItemFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.item.SimpleItemFilterProcessor; import com.evolveum.midpoint.repo.sqlbase.filtering.item.TimestampItemFilterProcessor; @@ -35,6 +36,7 @@ import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.SchemaException; /** * Common supertype for mapping items/attributes between schema (prism) classes and tables. @@ -43,9 +45,8 @@ * related to-many detail tables. * * The main goal of this type is to map object query conditions and ORDER BY to SQL. - * Transformation between schema/prism objects and repository objects (row beans or tuples) is - * delegated to {@link SqlTransformer}. - * Objects of various {@link QueryTableMapping} subclasses are factories for the transformer. + * Mappings also takes care of transformation between schema/prism objects and repository objects + * (row beans or tuples). * * Other important functions of mapping: * @@ -59,6 +60,10 @@ * That's why these methods have flexible schema type parameter. * Because such nested mapping still uses the same table, types `Q` and `R` remain the same. * + * Mapping for tables is initialized once and requires {@link SqlRepoContext}. + * The mapping is accessible by static `get()` method; if multiple mapping instances exist + * for the same type the method uses suffix to differentiate them. + * * @param schema type * @param type of entity path * @param row type related to the {@link Q} @@ -68,6 +73,7 @@ public abstract class QueryTableMapping schemaType, - @NotNull Class queryType) { + @NotNull Class queryType, + @NotNull SqlRepoContext repositoryContext) { super(schemaType, queryType); this.tableName = tableName; this.defaultAliasName = defaultAliasName; + this.repositoryContext = repositoryContext; } /** @@ -219,6 +227,10 @@ public String defaultAliasName() { return defaultAliasName; } + public SqlRepoContext repositoryContext() { + return repositoryContext; + } + /** * Creates new alias (entity path instance) with a defined name. * You can also use {@link #defaultAlias()} if one alias in a query is enough. @@ -253,18 +265,6 @@ public synchronized Q defaultAlias() { return defaultAlias; } - /** - * Creates {@link SqlTransformer} of row bean to schema type, override if provided. - * TODO: rethink/confirm this create mechanism, currently the SqlTransformerSupport is managed - * component without any other state and so are transformers, perhaps we can pre-create them - * or cache (I don't like the sound of that). On the other hand they are really lightweight - * and short lived helpers too, so it shouldn't be a real GC problem. - */ - public SqlTransformer createTransformer( - SqlTransformerSupport transformerSupport) { - throw new UnsupportedOperationException("Bean transformer not supported for " + queryType()); - } - /** * Returns collection of all registered {@link SqlDetailFetchMapper}s. */ @@ -324,6 +324,35 @@ public R newRowObject() { "Row bean creation not implemented for query type " + queryType().getName()); } + /** + * Transforms row of {@link R} type to schema type {@link S}. + * If pre-generated bean is used as row it does not include extension (dynamic) columns, + * which is OK if extension columns are used only for query and their information + * is still contained in the object somehow else (e.g. full object LOB). + * + * Alternative would be dynamically generated list of select expressions and transforming + * row to M object directly from {@link com.querydsl.core.Tuple}. + */ + public abstract S toSchemaObject(R row) throws SchemaException; + + /** + * Transforms row Tuple containing attributes of {@link R} to schema type {@link S}. + * Entity path can be used to access tuple elements. + * This allows loading also dynamically defined columns (like extensions). + */ + public abstract S toSchemaObject(Tuple row, Q entityPath, + Collection> options) + throws SchemaException; + + public S toSchemaObjectSafe(Tuple tuple, Q entityPath, + Collection> options) { + try { + return toSchemaObject(tuple, entityPath, options); + } catch (SchemaException e) { + throw new RepositoryMappingException(e); + } + } + @Override public String toString() { return "QueryTableMapping{" + diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/RepositoryMappingException.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/RepositoryMappingException.java new file mode 100644 index 00000000000..bec86faba69 --- /dev/null +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/RepositoryMappingException.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2021 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.repo.sqlbase.mapping; + +/** + * Runtime exception wrapping other exception that occurred during object transformation + * inside mapping (e.g. tuple to schema object). + */ +public class RepositoryMappingException extends RuntimeException { + + public RepositoryMappingException(Throwable cause) { + super(cause); + } +} diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/SqlTransformer.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/SqlTransformer.java deleted file mode 100644 index 20e71732c62..00000000000 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/SqlTransformer.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2010-2021 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.repo.sqlbase.mapping; - -import java.util.Collection; - -import com.querydsl.core.Tuple; - -import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.SelectorOptions; -import com.evolveum.midpoint.util.exception.SchemaException; - -/** - * Contract for SQL transformers translating from query beans or tuples to model types. - * Concrete transformers may do more than pure transformation, they can also help with storing - * related entities (e.g. parts of the object aggregate). - * The implemented methods may vary wildly as some are used by very generic mechanisms (query - * interpreter) while others are called on concrete transformer of known type needed in particular - * situation. - * - * @param schema type - * @param type of entity path - * @param type of the transformed data, a row bean - */ -public interface SqlTransformer, R> { - - /** - * Transforms row of {@link R} type to schema type {@link S}. - * If pre-generated bean is used as row it does not include extension (dynamic) columns, - * which is OK if extension columns are used only for query and their information - * is still contained in the object somehow else (e.g. full object LOB). - *

- * Alternative would be dynamically generated list of select expressions and transforming - * row to M object directly from {@link com.querydsl.core.Tuple}. - */ - S toSchemaObject(R row) throws SchemaException; - - /** - * Transforms row Tuple containing attributes of {@link R} to schema type {@link S}. - * Entity path can be used to access tuple elements. - * This allows loading also dynamically defined columns (like extensions). - */ - S toSchemaObject(Tuple row, Q entityPath, - Collection> options) - throws SchemaException; - - default S toSchemaObjectSafe(Tuple tuple, Q entityPath, Collection> options) { - try { - return toSchemaObject(tuple, entityPath, options); - } catch (SchemaException e) { - throw new SqlTransformationException(e); - } - } - - class SqlTransformationException extends RuntimeException { - public SqlTransformationException(Throwable cause) { - super(cause); - } - } -} diff --git a/testing/story/pom.xml b/testing/story/pom.xml index 31af5e5bab1..0f7d7023445 100644 --- a/testing/story/pom.xml +++ b/testing/story/pom.xml @@ -181,6 +181,10 @@ ch.qos.logback logback-classic + + org.apache.velocity + velocity + diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/DetailsOutputFile.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/DetailsOutputFile.java new file mode 100644 index 00000000000..5da5a5c7f2d --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/DetailsOutputFile.java @@ -0,0 +1,34 @@ +package com.evolveum.midpoint.testing.story.sysperf; + +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.*; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import com.evolveum.midpoint.schema.util.task.TaskOperationStatsUtil; +import com.evolveum.midpoint.schema.util.task.TaskPerformanceInformation; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; + +class DetailsOutputFile { + + private final PrintWriter writer; + + DetailsOutputFile() throws IOException { + writer = new PrintWriter(new FileWriter(getFile())); + } + + private File getFile() { + return new File(TARGET_DIR, START + "-" + OTHER_PARAMETERS.label + "-details.txt"); + } + + void logTaskFinish(String desc, TaskType taskAfter, TaskPerformanceInformation performanceInformation) { + writer.printf("********** FINISHED: %s **********\n\n", desc); + writer.println(TaskOperationStatsUtil.format(taskAfter.getOperationStats())); + writer.println(); + writer.println(performanceInformation.debugDump()); + writer.println(); + writer.flush(); + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/ImportConfiguration.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/ImportConfiguration.java new file mode 100644 index 00000000000..0e655deaf3a --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/ImportConfiguration.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.TARGET_DIR; +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.TEST_DIR; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import com.evolveum.midpoint.test.DummyTestResource; +import com.evolveum.midpoint.test.TestResource; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; + +class ImportConfiguration { + + private static final String PROP = "import"; + private static final String PROP_THREADS = PROP + ".threads"; + private static final String PROP_NO_OP_RUNS = PROP + ".no-op-runs"; + + private static final File TASK_TEMPLATE_FILE = new File(TEST_DIR, "task-import.vm.xml"); + + private final int threads; + private final int noOpRuns; + + private final List> generatedTasks; + + private ImportConfiguration() { + threads = Integer.parseInt(System.getProperty(PROP_THREADS, "0")); + noOpRuns = Integer.parseInt(System.getProperty(PROP_NO_OP_RUNS, "1")); + + generatedTasks = generateTasks(); + } + + int getThreads() { + return threads; + } + + int getNoOpRuns() { + return noOpRuns; + } + + List> getGeneratedTasks() { + return generatedTasks; + } + + @Override + public String toString() { + return "ImportConfiguration{" + + "threads=" + threads + + ", noOpRuns=" + noOpRuns + + '}'; + } + + public static ImportConfiguration setup() { + ImportConfiguration configuration = new ImportConfiguration(); + System.out.println("Import: " + configuration); + return configuration; + } + + private List> generateTasks() { + List> tasks = new ArrayList<>(); + List sourceResources = TestSystemPerformance.SOURCES_CONFIGURATION.getGeneratedResources(); + for (int i = 0; i < sourceResources.size(); i++) { + String taskOid = UUID.randomUUID().toString(); + tasks.add(new TestResource<>(TARGET_DIR, createFile(i, sourceResources.get(i), taskOid), taskOid)); + } + return tasks; + } + + private String createFile(int index, DummyTestResource resource, String taskOid) { + String generatedFileName = String.format("generated-task-import-%03d.xml", index); + + File generated = new File(TARGET_DIR, generatedFileName); + VelocityGenerator.generate(TASK_TEMPLATE_FILE, generated, + Map.of("taskOid", taskOid, + "index", String.format("%03d", index), + "resourceOid", resource.oid, + "workerThreads", threads)); + + return generatedFileName; + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/OtherParameters.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/OtherParameters.java new file mode 100644 index 00000000000..c2d45a37602 --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/OtherParameters.java @@ -0,0 +1,64 @@ +package com.evolveum.midpoint.testing.story.sysperf; + +import org.jetbrains.annotations.NotNull; + +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.*; + +public class OtherParameters { + + private static final String PROP_LABEL = "label"; + private static final String PROP_TASK_TIMEOUT = "taskTimeout"; + + final String label; + final int taskTimeout; + + private OtherParameters() { + this.label = System.getProperty(PROP_LABEL, createDefaultLabel()); + this.taskTimeout = Integer.parseInt(System.getProperty(PROP_TASK_TIMEOUT, "1800000")); // 30 minutes + } + + private String createDefaultLabel() { + return SOURCES_CONFIGURATION.getNumberOfResources() + "s-" + + getSourceMappingsLabel() + "-" + + TARGETS_CONFIGURATION.getNumberOfResources() + "t-" + + getTargetMappingsLabel() + "-" + + getAssignmentsLabel() + ; + } + + @NotNull + private String getSourceMappingsLabel() { + int single = SOURCES_CONFIGURATION.getSingleValuedMappings(); + int multi = SOURCES_CONFIGURATION.getMultiValuedMappings(); + if (single == multi) { + return single + "m"; + } else { + return single + "sm-" + multi + "mm"; + } + } + + @NotNull + private String getTargetMappingsLabel() { + int single = TARGETS_CONFIGURATION.getSingleValuedMappings(); + int multi = TARGETS_CONFIGURATION.getMultiValuedMappings(); + if (single == multi) { + return single + "m"; + } else { + return single + "sm" + multi + "mm"; + } + } + + private String getAssignmentsLabel() { + int min = ROLES_CONFIGURATION.getNumberOfAssignmentsMin(); + int max = ROLES_CONFIGURATION.getNumberOfAssignmentsMax(); + if (min == max) { + return min + "a"; + } else { + return min + "-" + max + "a"; + } + } + + static OtherParameters setup() { + return new OtherParameters(); + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/ProgressOutputFile.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/ProgressOutputFile.java new file mode 100644 index 00000000000..54e05b49f48 --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/ProgressOutputFile.java @@ -0,0 +1,34 @@ +package com.evolveum.midpoint.testing.story.sysperf; + +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.*; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import com.evolveum.midpoint.task.api.Task; + +class ProgressOutputFile { + + private final PrintWriter writer; + + ProgressOutputFile() throws IOException { + writer = new PrintWriter(new FileWriter(getFile())); + writer.println("test;time;progress"); + } + + private File getFile() { + return new File(TARGET_DIR, START + "-" + OTHER_PARAMETERS.label + "-progress.csv"); + } + + void recordProgress(String label, Task task) { + long start = task.getLastRunStartTimestamp(); + Long lastFinish = task.getLastRunFinishTimestamp(); + long thisFinish = lastFinish != null && lastFinish > start ? lastFinish : System.currentTimeMillis(); + long running = thisFinish - start; + long progress = task.getProgress(); + writer.println(label + task.getName() + ";" + running + ";" + progress); + writer.flush(); + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/RecomputationConfiguration.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/RecomputationConfiguration.java new file mode 100644 index 00000000000..08aa4d63310 --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/RecomputationConfiguration.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.TARGET_DIR; +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.TEST_DIR; + +import java.io.File; +import java.util.Map; + +import com.evolveum.midpoint.test.TestResource; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; + +class RecomputationConfiguration { + + private static final String PROP = "recomputation"; + private static final String PROP_THREADS = PROP + ".threads"; + + private static final File TASK_TEMPLATE_FILE = new File(TEST_DIR, "task-recomputation.vm.xml"); + + private static final String RECOMPUTE_TASK_OID = "f5920848-6c8f-4eda-ae26-2b961d6dae1b"; + + private final int threads; + + private final TestResource generatedTask; + + private RecomputationConfiguration() { + threads = Integer.parseInt(System.getProperty(PROP_THREADS, "0")); + generatedTask = generateTask(); + } + + int getThreads() { + return threads; + } + + TestResource getGeneratedTask() { + return generatedTask; + } + + @Override + public String toString() { + return "RecomputationConfiguration{" + + "threads=" + threads + + '}'; + } + + public static RecomputationConfiguration setup() { + RecomputationConfiguration configuration = new RecomputationConfiguration(); + System.out.println("Recompute: " + configuration); + return configuration; + } + + private TestResource generateTask() { + return new TestResource<>(TARGET_DIR, createFile(), RECOMPUTE_TASK_OID); + } + + private String createFile() { + String generatedFileName = "generated-task-recompute.xml"; + + File generated = new File(TARGET_DIR, generatedFileName); + VelocityGenerator.generate(TASK_TEMPLATE_FILE, generated, + Map.of("workerThreads", threads)); + + return generatedFileName; + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/ReconciliationConfiguration.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/ReconciliationConfiguration.java new file mode 100644 index 00000000000..4bb27dbf3d7 --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/ReconciliationConfiguration.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import com.evolveum.midpoint.test.DummyTestResource; +import com.evolveum.midpoint.test.TestResource; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.TARGET_DIR; +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.TEST_DIR; + +class ReconciliationConfiguration { + + private static final String PROP = "reconciliation"; + private static final String PROP_THREADS = PROP + ".threads"; + private static final String PROP_RUNS = PROP + ".runs"; + + private static final File TASK_TEMPLATE_FILE = new File(TEST_DIR, "task-reconciliation.vm.xml"); + + private final int threads; + private final int runs; + + private final List> generatedTasks; + + private ReconciliationConfiguration() { + threads = Integer.parseInt(System.getProperty(PROP_THREADS, "0")); + runs = Integer.parseInt(System.getProperty(PROP_RUNS, "1")); + + generatedTasks = generateTasks(); + } + + int getThreads() { + return threads; + } + + int getRuns() { + return runs; + } + + List> getGeneratedTasks() { + return generatedTasks; + } + + @Override + public String toString() { + return "ReconciliationConfiguration{" + + "threads=" + threads + + ", runs=" + runs + + '}'; + } + + public static ReconciliationConfiguration setup() { + ReconciliationConfiguration configuration = new ReconciliationConfiguration(); + System.out.println("Import: " + configuration); + return configuration; + } + + private List> generateTasks() { + List> tasks = new ArrayList<>(); + List sourceResources = TestSystemPerformance.SOURCES_CONFIGURATION.getGeneratedResources(); + for (int i = 0; i < sourceResources.size(); i++) { + String taskOid = UUID.randomUUID().toString(); + tasks.add(new TestResource<>(TARGET_DIR, createFile(i, sourceResources.get(i), taskOid), taskOid)); + } + return tasks; + } + + private String createFile(int index, DummyTestResource resource, String taskOid) { + String generatedFileName = String.format("generated-task-reconciliation-%03d.xml", index); + + File generated = new File(TARGET_DIR, generatedFileName); + VelocityGenerator.generate(TASK_TEMPLATE_FILE, generated, + Map.of("taskOid", taskOid, + "index", String.format("%03d", index), + "resourceOid", resource.oid, + "workerThreads", threads)); + + return generatedFileName; + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/RolesConfiguration.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/RolesConfiguration.java new file mode 100644 index 00000000000..b758909f1ab --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/RolesConfiguration.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.*; + +import static java.util.Collections.emptyList; + +import java.io.File; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import com.evolveum.midpoint.test.TestResource; +import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; + +import org.jetbrains.annotations.NotNull; + +class RolesConfiguration { + + private static final String PROP = "roles"; + private static final String PROP_BUSINESS = PROP + ".business"; + private static final String PROP_BUSINESS_COUNT = PROP_BUSINESS + ".count"; + private static final String PROP_TECHNICAL = PROP + ".technical"; + private static final String PROP_TECHNICAL_COUNT = PROP_TECHNICAL + ".count"; + private static final String PROP_ASSIGNMENTS = PROP + ".assignments"; + private static final String PROP_ASSIGNMENTS_COUNT = PROP_ASSIGNMENTS + ".count"; + private static final String PROP_ASSIGNMENTS_MIN = PROP_ASSIGNMENTS + ".min"; + private static final String PROP_ASSIGNMENTS_MAX = PROP_ASSIGNMENTS + ".max"; + private static final String PROP_INDUCEMENTS = PROP + ".inducements"; + private static final String PROP_INDUCEMENTS_COUNT = PROP_INDUCEMENTS + ".count"; + private static final String PROP_INDUCEMENTS_MIN = PROP_INDUCEMENTS + ".min"; + private static final String PROP_INDUCEMENTS_MAX = PROP_INDUCEMENTS + ".max"; + + private static final File BUSINESS_ROLE_TEMPLATE_FILE = new File(TEST_DIR, "role-business.vm.xml"); + private static final File TECHNICAL_ROLE_TEMPLATE_FILE = new File(TEST_DIR, "role-technical.vm.xml"); + + private final int numberOfBusinessRoles; + private final int numberOfTechnicalRoles; + private final int numberOfAssignmentsMin; + private final int numberOfAssignmentsMax; + private final int numberOfInducementsMin; + private final int numberOfInducementsMax; + + private final List> generatedBusinessRoles; + private final List> generatedTechnicalRoles; + + private RolesConfiguration() { + numberOfBusinessRoles = Integer.parseInt(System.getProperty(PROP_BUSINESS_COUNT, "2")); + numberOfTechnicalRoles = Integer.parseInt(System.getProperty(PROP_TECHNICAL_COUNT, "2")); + String assignmentsCount = System.getProperty(PROP_ASSIGNMENTS_COUNT); + if (assignmentsCount != null) { + numberOfAssignmentsMax = numberOfAssignmentsMin = Integer.parseInt(assignmentsCount); + } else { + numberOfAssignmentsMin = Integer.parseInt(System.getProperty(PROP_ASSIGNMENTS_MIN, "1")); + numberOfAssignmentsMax = Integer.parseInt(System.getProperty(PROP_ASSIGNMENTS_MAX, String.valueOf(numberOfAssignmentsMin))); + } + String inducementsCount = System.getProperty(PROP_INDUCEMENTS_COUNT); + if (inducementsCount != null) { + numberOfInducementsMax = numberOfInducementsMin = Integer.parseInt(inducementsCount); + } else { + numberOfInducementsMin = Integer.parseInt(System.getProperty(PROP_INDUCEMENTS_MIN, "1")); + numberOfInducementsMax = Integer.parseInt(System.getProperty(PROP_INDUCEMENTS_MAX, String.valueOf(numberOfInducementsMin))); + } + + generatedTechnicalRoles = generateTechnicalRoles(); + generatedBusinessRoles = generateBusinessRoles(); + } + + int getNumberOfBusinessRoles() { + return numberOfBusinessRoles; + } + + int getNumberOfTechnicalRoles() { + return numberOfTechnicalRoles; + } + + int getNumberOfAssignmentsMin() { + return numberOfAssignmentsMin; + } + + int getNumberOfAssignmentsMax() { + return numberOfAssignmentsMax; + } + + int getNumberOfInducementsMin() { + return numberOfInducementsMin; + } + + int getNumberOfInducementsMax() { + return numberOfInducementsMax; + } + + List> getGeneratedBusinessRoles() { + return generatedBusinessRoles; + } + + List> getGeneratedTechnicalRoles() { + return generatedTechnicalRoles; + } + + @Override + public String toString() { + return "RolesConfiguration{" + + "numberOfBusinessRoles=" + numberOfBusinessRoles + + ", numberOfTechnicalRoles=" + numberOfTechnicalRoles + + ", numberOfAssignmentsMin=" + numberOfAssignmentsMin + + ", numberOfAssignmentsMax=" + numberOfAssignmentsMax + + ", numberOfInducementsMin=" + numberOfInducementsMin + + ", numberOfInducementsMax=" + numberOfInducementsMax + + '}'; + } + + public static RolesConfiguration setup() { + RolesConfiguration configuration = new RolesConfiguration(); + System.out.println("Roles: " + configuration); + return configuration; + } + + private List> generateTechnicalRoles() { + List> roles = new ArrayList<>(); + for (int i = 0; i < numberOfTechnicalRoles; i++) { + String oid = UUID.randomUUID().toString(); + String fileName = createTechnicalRoleDefinition(i, oid); + roles.add(new TestResource<>(TARGET_DIR, fileName, oid)); + } + return roles; + } + + private String createTechnicalRoleDefinition(int index, String oid) { + String fileName = String.format("generated-technical-role-%04d.xml", index); + String resourceOid; + if (TARGETS_CONFIGURATION.getNumberOfResources() > 0) { + resourceOid = TARGETS_CONFIGURATION.getGeneratedResources() + .get(index % TARGETS_CONFIGURATION.getNumberOfResources()) + .oid; + } else { + resourceOid = ""; + } + + File generated = new File(TARGET_DIR, fileName); + VelocityGenerator.generate(TECHNICAL_ROLE_TEMPLATE_FILE, generated, + Map.of("oid", oid, + "index", String.format("%04d", index), + "resourceOid", resourceOid)); + + return fileName; + } + + private List> generateBusinessRoles() { + List> roles = new ArrayList<>(); + for (int i = 0; i < numberOfBusinessRoles; i++) { + String oid = UUID.randomUUID().toString(); + List inducedOidList = createInducedOidList(); + String fileName = createBusinessRoleDefinition(i, oid, inducedOidList); + roles.add(new TestResource<>(TARGET_DIR, fileName, oid)); + } + return roles; + } + + private List createInducedOidList() { + int size = randomFromInterval(numberOfInducementsMin, numberOfInducementsMax); + if (size <= 0) { + return emptyList(); + } else { + List technicalRolesOidList = generatedTechnicalRoles.stream() + .map(r -> r.oid) + .collect(Collectors.toList()); + return randomFromList(technicalRolesOidList, size); + } + } + + @NotNull + private List randomFromList(List technicalRolesOidList, int size) { + Collections.shuffle(technicalRolesOidList); + return technicalRolesOidList.subList(0, size); + } + + private int randomFromInterval(int min, int max) { + //noinspection OptionalGetWithoutIsPresent + return new Random() + .ints(min, max + 1) + .findFirst().getAsInt(); + } + + private String createBusinessRoleDefinition(int index, String oid, List inducedOidList) { + String fileName = String.format("generated-business-role-%04d.xml", index); + + File generated = new File(TARGET_DIR, fileName); + VelocityGenerator.generate(BUSINESS_ROLE_TEMPLATE_FILE, generated, + Map.of("oid", oid, + "name", getBusinessRoleName(index), + "inducedOidList", inducedOidList)); + + return fileName; + } + + private String getBusinessRoleName(int index) { + return String.format("business-%04d", index); + } + + /** + * Generates random names of business roles for an account. + */ + List getRolesForAccount() { + int assignedRoles = randomFromInterval(numberOfAssignmentsMin, numberOfAssignmentsMax); + if (assignedRoles == 0) { + return List.of(); + } else { + List businessRolesNames = IntStream.range(0, numberOfBusinessRoles) + .mapToObj(this::getBusinessRoleName) + .collect(Collectors.toList()); + return randomFromList(businessRolesNames, assignedRoles); + } + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SchemaConfiguration.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SchemaConfiguration.java new file mode 100644 index 00000000000..b9f6fcbdaf7 --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SchemaConfiguration.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.evolveum.midpoint.test.util.MidPointTestConstants.TARGET_DIR_PATH; +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.TEST_DIR; + +class SchemaConfiguration { + + private static final String PROP = "schema"; + private static final String PROP_SINGLE_VALUED_PROPERTIES = PROP + ".single-valued-properties"; + private static final String PROP_MULTI_VALUED_PROPERTIES = PROP + ".multi-valued-properties"; + private static final String PROP_INDEXED_PERCENTAGE = PROP + ".indexed-percentage"; + + private static final File SCHEMA_TEMPLATE_FILE = new File(TEST_DIR, "schema.vm.xsd"); + + private static final String GENERATED_SCHEMA_DIR_PATH = TARGET_DIR_PATH + "/schema"; + private static final File GENERATED_SCHEMA_DIR = new File(GENERATED_SCHEMA_DIR_PATH); + private static final File GENERATED_SCHEMA_FILE = new File(GENERATED_SCHEMA_DIR, "schema.xsd"); + + private static final String P_SINGLE_PATTERN = "p-single-%04d"; + private static final String P_MULTI_PATTERN = "p-multi-%04d"; + + private final int singleValuedProperties; + private final int multiValuedProperties; + private final int indexedPercentage; + + private SchemaConfiguration() { + this.singleValuedProperties = Integer.parseInt(System.getProperty(PROP_SINGLE_VALUED_PROPERTIES, "100")); + this.multiValuedProperties = Integer.parseInt(System.getProperty(PROP_MULTI_VALUED_PROPERTIES, "10")); + this.indexedPercentage = Integer.parseInt(System.getProperty(PROP_INDEXED_PERCENTAGE, "0")); + + generateSchemaFile(); + } + + int getSingleValuedProperties() { + return singleValuedProperties; + } + + int getMultiValuedProperties() { + return multiValuedProperties; + } + + int getIndexedPercentage() { + return indexedPercentage; + } + + @Override + public String toString() { + return "SchemaConfiguration{" + + "singleValuedProperties=" + singleValuedProperties + + ", multiValuedProperties=" + multiValuedProperties + + ", indexedPercentage=" + indexedPercentage + + '}'; + } + + public static SchemaConfiguration setup() { + System.setProperty("midpoint.global.extensionDir", GENERATED_SCHEMA_DIR_PATH); + + SchemaConfiguration configuration = new SchemaConfiguration(); + System.out.println("Schema: " + configuration); + return configuration; + } + + private void generateSchemaFile() { + + List itemDefList = new ArrayList<>(); + IndexedSelector indexedSelector = new IndexedSelector(); + for (int i = 0; i < singleValuedProperties; i++) { + String name = String.format(P_SINGLE_PATTERN, i); + boolean indexed = indexedSelector.getNext(); + itemDefList.add(new ItemDef(name, "1", indexed)); + } + indexedSelector.reset(); + for (int i = 0; i < multiValuedProperties; i++) { + String name = String.format(P_MULTI_PATTERN, i); + boolean indexed = indexedSelector.getNext(); + itemDefList.add(new ItemDef(name, "unbounded", indexed)); + } + + //noinspection ResultOfMethodCallIgnored + GENERATED_SCHEMA_DIR.mkdirs(); + + VelocityGenerator.generate(SCHEMA_TEMPLATE_FILE, GENERATED_SCHEMA_FILE, + Map.of("itemList", itemDefList)); + } + + public static class ItemDef { + private final String name; + private final String maxOccurs; + private final boolean indexed; + + private ItemDef(String name, String maxOccurs, boolean indexed) { + this.name = name; + this.maxOccurs = maxOccurs; + this.indexed = indexed; + } + + public String getName() { + return name; + } + + public String getMaxOccurs() { + return maxOccurs; + } + + public boolean isIndexed() { + return indexed; + } + } + + private class IndexedSelector { + private int counter; + private boolean getNext() { + counter += indexedPercentage; + if (counter >= 100) { + counter -= 100; + return true; + } else { + return false; + } + } + + public void reset() { + counter = 0; + } + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SourceInitializer.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SourceInitializer.java new file mode 100644 index 00000000000..a2eabd9a591 --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SourceInitializer.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import com.evolveum.icf.dummy.resource.ConflictException; +import com.evolveum.icf.dummy.resource.DummyAccount; +import com.evolveum.icf.dummy.resource.ObjectAlreadyExistsException; +import com.evolveum.icf.dummy.resource.SchemaViolationException; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.DummyTestResource; + +import org.apache.commons.lang3.RandomStringUtils; + +import java.io.FileNotFoundException; +import java.net.ConnectException; +import java.util.List; + +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.ROLES_CONFIGURATION; +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.SOURCES_CONFIGURATION; + +public class SourceInitializer { + + private final TestSystemPerformance test; + private final List resources; + private final Task initTask; + + private static final String ACCOUNT_NAME = "u-%08d"; + + SourceInitializer(TestSystemPerformance test, List resources, Task initTask) { + this.test = test; + this.resources = resources; + this.initTask = initTask; + } + + public void run(OperationResult result) throws Exception { + boolean primary = true; + for (DummyTestResource resource : resources) { + initializeResource(resource, result); + createAccounts(resource, primary); + primary = false; + } + } + + private void initializeResource(DummyTestResource resource, OperationResult result) throws Exception { + test.initDummyResource(resource, initTask, result); + } + + private void createAccounts(DummyTestResource resource, boolean primary) + throws ConflictException, FileNotFoundException, SchemaViolationException, + ObjectAlreadyExistsException, InterruptedException, ConnectException { + for (int u = 0; u < SOURCES_CONFIGURATION.getNumberOfAccounts(); u++) { + createAccount(u, resource, primary); + } + } + + private void createAccount(int u, DummyTestResource resource, boolean primary) + throws ObjectAlreadyExistsException, SchemaViolationException, ConnectException, FileNotFoundException, + ConflictException, InterruptedException { + String name = getAccountName(u); + DummyAccount account = resource.controller.addAccount(name); + if (primary) { + addAttributes(account, SourcesConfiguration.A_SINGLE_NAME, SOURCES_CONFIGURATION.getSingleValuedMappings(), 1); + addRoles(account); + } + addAttributes(account, SourcesConfiguration.A_MULTI_NAME, SOURCES_CONFIGURATION.getMultiValuedMappings(), + SOURCES_CONFIGURATION.getAttributeValues()); + } + + private void addRoles(DummyAccount account) throws ConflictException, FileNotFoundException, SchemaViolationException, + InterruptedException, ConnectException { + account.addAttributeValues(SourcesConfiguration.A_ROLE, ROLES_CONFIGURATION.getRolesForAccount()); + } + + static String getAccountName(int u) { + return String.format(ACCOUNT_NAME, u); + } + + private void addAttributes(DummyAccount account, String namePattern, int attrs, int values) throws ConflictException, + FileNotFoundException, SchemaViolationException, InterruptedException, ConnectException { + for (int a = 0; a < attrs; a++) { + String attrName = String.format(namePattern, a); + for (int v = 0; v < values; v++) { + account.addAttributeValue(attrName, RandomStringUtils.random(10, true, true)); + } + } + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SourcesConfiguration.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SourcesConfiguration.java new file mode 100644 index 00000000000..7d9c3d15105 --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SourcesConfiguration.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import com.evolveum.icf.dummy.resource.ConflictException; +import com.evolveum.icf.dummy.resource.DummyObjectClass; +import com.evolveum.icf.dummy.resource.SchemaViolationException; +import com.evolveum.midpoint.test.DummyResourceContoller; +import com.evolveum.midpoint.test.DummyTestResource; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.FileNotFoundException; +import java.net.ConnectException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.TARGET_DIR; +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.TEST_DIR; + +import static java.util.Collections.emptyList; + +class SourcesConfiguration { + + private static final String PROP = "sources"; + private static final String PROP_RESOURCES = PROP + ".resources"; + private static final String PROP_ACCOUNTS = PROP + ".accounts"; + private static final String PROP_SINGLE_MAPPINGS = PROP + ".single-mappings"; + private static final String PROP_MULTI_MAPPINGS = PROP + ".multi-mappings"; + private static final String PROP_MULTI_ATTR_VALUES = PROP + ".multi-attr-values"; + + private static final String RESOURCE_INSTANCE_TEMPLATE = "source-%03d"; + static final String A_SINGLE_NAME = "a-single-%04d"; + static final String A_MULTI_NAME = "a-multi-%04d"; + static final String A_ROLE = "role"; + + private static final File RESOURCE_TEMPLATE_FILE = new File(TEST_DIR, "resource-source.vm.xml"); + + private final int numberOfResources; + private final int numberOfAccounts; + private final int singleValuedMappings; + private final int multiValuedMappings; + private final int attributeValues; + + private final List generatedResources; + + private SourcesConfiguration() { + numberOfResources = Integer.parseInt(System.getProperty(PROP_RESOURCES, "1")); + numberOfAccounts = Integer.parseInt(System.getProperty(PROP_ACCOUNTS, "10")); + singleValuedMappings = Integer.parseInt(System.getProperty(PROP_SINGLE_MAPPINGS, "1")); + multiValuedMappings = Integer.parseInt(System.getProperty(PROP_MULTI_MAPPINGS, "1")); + attributeValues = Integer.parseInt(System.getProperty(PROP_MULTI_ATTR_VALUES, "5")); + + generatedResources = generateDummyTestResources(); + } + + int getNumberOfResources() { + return numberOfResources; + } + + public int getNumberOfAccounts() { + return numberOfAccounts; + } + + int getSingleValuedMappings() { + return singleValuedMappings; + } + + int getMultiValuedMappings() { + return multiValuedMappings; + } + + public int getAttributeValues() { + return attributeValues; + } + + List getGeneratedResources() { + return generatedResources; + } + + @Override + public String toString() { + return "SourcesConfiguration{" + + "numberOfResources=" + numberOfResources + + ", numberOfAccounts=" + numberOfAccounts + + ", singleValuedMappings=" + singleValuedMappings + + ", multiValuedMappings=" + multiValuedMappings + + ", attributeValues=" + attributeValues + + '}'; + } + + public static SourcesConfiguration setup() { + SourcesConfiguration configuration = new SourcesConfiguration(); + System.out.println("Sources: " + configuration); + return configuration; + } + + private List generateDummyTestResources() { + List resources = new ArrayList<>(); + for (int i = 0; i < numberOfResources; i++) { + boolean primary = i == 0; + String oid = UUID.randomUUID().toString(); + String resourceDefinitionFile = createResourceDefinition(i, oid, primary); + resources.add(new DummyTestResource(TARGET_DIR, resourceDefinitionFile, oid, getResourceInstance(i), + controller -> { + if (primary) { + createAttributes(controller, A_SINGLE_NAME, singleValuedMappings, false); + controller.addAttrDef(controller.getDummyResource().getAccountObjectClass(), + A_ROLE, String.class, false, true); + } + createAttributes(controller, A_MULTI_NAME, multiValuedMappings, true); + })); + } + return resources; + } + + private String createResourceDefinition(int index, String oid, boolean primary) { + String generatedFileName = String.format("generated-resource-source-%03d.xml", index); + + File generated = new File(TARGET_DIR, generatedFileName); + VelocityGenerator.generate(RESOURCE_TEMPLATE_FILE, generated, + Map.of("resourceOid", oid, + "resourceInstance", getResourceInstance(index), + "multiValuedIndexList", Util.createIndexList(multiValuedMappings), + "singleValuedIndexList", primary ? + Util.createIndexList(singleValuedMappings) : emptyList(), + "primary", primary)); + return generatedFileName; + } + + @NotNull + private String getResourceInstance(int i) { + return String.format(RESOURCE_INSTANCE_TEMPLATE, i); + } + + private void createAttributes(DummyResourceContoller controller, String name, int number, boolean multi) + throws ConflictException, FileNotFoundException, SchemaViolationException, InterruptedException, ConnectException { + DummyObjectClass objectClass = controller.getDummyResource().getAccountObjectClass(); + for (int i = 0; i < number; i++) { + controller.addAttrDef(objectClass, String.format(name, i), String.class, false, multi); + } + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SummaryOutputFile.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SummaryOutputFile.java new file mode 100644 index 00000000000..c6834e0d5cf --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/SummaryOutputFile.java @@ -0,0 +1,52 @@ +package com.evolveum.midpoint.testing.story.sysperf; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Date; + +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.*; + +class SummaryOutputFile { + + private final PrintWriter writer; + + SummaryOutputFile() throws IOException { + writer = new PrintWriter(new FileWriter(getFile())); + } + + private File getFile() { + return new File(TARGET_DIR, START + "-" + OTHER_PARAMETERS.label + "-summary.txt"); + } + + void logStart() { + writer.println("Started: " + new Date(START) + " (" + START + ")"); + writer.println("Label: " + OTHER_PARAMETERS.label); + writer.println(); + writer.printf("Schema: %s\n", SCHEMA_CONFIGURATION); + writer.printf("Sources: %s\n", SOURCES_CONFIGURATION); + writer.printf("Targets: %s\n", TARGETS_CONFIGURATION); + writer.printf("Roles: %s\n", ROLES_CONFIGURATION); + writer.printf("Import: %s\n", IMPORTS_CONFIGURATION); + writer.printf("Reconciliation: %s\n", RECONCILIATIONS_CONFIGURATION); + writer.printf("Recomputation: %s\n\n", RECOMPUTATION_CONFIGURATION); + writer.flush(); + } + + void logTaskFinish(String desc, long executionTime, double timePerAccount) { + writer.printf("********** FINISHED: %s **********\n\n", desc); + writer.printf("Task execution time: %,d ms\n", executionTime); + writer.printf("Time per account: %,.1f ms\n\n", timePerAccount); + writer.flush(); + } + + void logFinish() { + long end = System.currentTimeMillis(); + writer.printf("Finished at: %s\n", new Date(end)); + long millis = end - START; + writer.printf("Took: %,.1f seconds = %.1f minutes\n", millis / 1000.0, millis / 60000.0); + writer.printf("Accounts: %,d\n", SOURCES_CONFIGURATION.getNumberOfAccounts()); + writer.flush(); + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/TargetInitializer.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/TargetInitializer.java new file mode 100644 index 00000000000..10a2fe57036 --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/TargetInitializer.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import java.util.List; + +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.DummyTestResource; + +public class TargetInitializer { + + private final TestSystemPerformance test; + private final List resources; + private final Task initTask; + + TargetInitializer(TestSystemPerformance test, List resources, Task initTask) { + this.test = test; + this.resources = resources; + this.initTask = initTask; + } + + public void run(OperationResult result) throws Exception { + for (DummyTestResource resource : resources) { + test.initDummyResource(resource, initTask, result); + } + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/TargetsConfiguration.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/TargetsConfiguration.java new file mode 100644 index 00000000000..2f4e0b739ec --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/TargetsConfiguration.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import static com.evolveum.midpoint.testing.story.sysperf.TestSystemPerformance.*; + +import java.io.File; +import java.io.FileNotFoundException; +import java.net.ConnectException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import com.evolveum.icf.dummy.resource.DummyGroup; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.icf.dummy.resource.ConflictException; +import com.evolveum.icf.dummy.resource.DummyObjectClass; +import com.evolveum.icf.dummy.resource.SchemaViolationException; +import com.evolveum.midpoint.test.DummyResourceContoller; +import com.evolveum.midpoint.test.DummyTestResource; + +class TargetsConfiguration { + + private static final String PROP = "targets"; + private static final String PROP_RESOURCES = PROP + ".resources"; + private static final String PROP_SINGLE_MAPPINGS = PROP + ".single-mappings"; + private static final String PROP_MULTI_MAPPINGS = PROP + ".multi-mappings"; + + private static final String RESOURCE_INSTANCE_TEMPLATE = "target-%03d"; + private static final String A_SINGLE_NAME = "a-single-%04d"; + private static final String A_MULTI_NAME = "a-multi-%04d"; + static final String A_MEMBERSHIP = "membership"; + + private static final File RESOURCE_TARGET_TEMPLATE_FILE = new File(TEST_DIR, "resource-target.vm.xml"); + private static final File ROLE_TARGETS_TEMPLATE_FILE = new File(TEST_DIR, "role-targets.vm.xml"); + + private final int numberOfResources; + private final int singleValuedMappings; + private final int multiValuedMappings; + + private final List generatedResources; + + private TargetsConfiguration() { + numberOfResources = Integer.parseInt(System.getProperty(PROP_RESOURCES, "0")); + singleValuedMappings = Integer.parseInt(System.getProperty(PROP_SINGLE_MAPPINGS, "0")); + multiValuedMappings = Integer.parseInt(System.getProperty(PROP_MULTI_MAPPINGS, "0")); + + generatedResources = generateDummyTestResources(); + generateRoleTargets(); + } + + int getSingleValuedMappings() { + return singleValuedMappings; + } + + int getMultiValuedMappings() { + return multiValuedMappings; + } + + int getNumberOfResources() { + return numberOfResources; + } + + public static TargetsConfiguration setup() { + TargetsConfiguration configuration = new TargetsConfiguration(); + System.out.println("Targets: " + configuration); + return configuration; + } + + @Override + public String toString() { + return "TargetsConfiguration{" + + "numberOfResources=" + numberOfResources + + ", singleValuedMappings=" + singleValuedMappings + + ", multiValuedMappings=" + multiValuedMappings + + '}'; + } + + private List generateDummyTestResources() { + List resources = new ArrayList<>(); + for (int i = 0; i < numberOfResources; i++) { + String oid = UUID.randomUUID().toString(); + String resourceDefinitionFile = createResourceDefinition(i, oid); + resources.add(new DummyTestResource(TARGET_DIR, resourceDefinitionFile, oid, getResourceInstance(i), + controller -> { + createAttributes(controller, A_SINGLE_NAME, singleValuedMappings, false); + createAttributes(controller, A_MULTI_NAME, multiValuedMappings, true); + controller.addAttrDef(controller.getDummyResource().getAccountObjectClass(), + A_MEMBERSHIP, String.class, false, true); + controller.addAttrDef(controller.getDummyResource().getGroupObjectClass(), + DummyGroup.ATTR_MEMBERS_NAME, String.class, false, true); + })); + } + return resources; + } + + private String createResourceDefinition(int index, String oid) { + String generatedFileName = String.format("generated-resource-target-%03d.xml", index); + + File generated = new File(TARGET_DIR, generatedFileName); + VelocityGenerator.generate(RESOURCE_TARGET_TEMPLATE_FILE, generated, + Map.of("resourceOid", oid, + "resourceInstance", getResourceInstance(index), + "multiValuedIndexList", Util.createIndexList(multiValuedMappings), + "singleValuedIndexList", Util.createIndexList(singleValuedMappings))); + + return generatedFileName; + } + + @NotNull + private String getResourceInstance(int i) { + return String.format(RESOURCE_INSTANCE_TEMPLATE, i); + } + + private void createAttributes(DummyResourceContoller controller, String name, int number, boolean multi) + throws ConflictException, FileNotFoundException, SchemaViolationException, InterruptedException, ConnectException { + DummyObjectClass objectClass = controller.getDummyResource().getAccountObjectClass(); + for (int i = 0; i < number; i++) { + controller.addAttrDef(objectClass, String.format(name, i), String.class, false, multi); + } + } + + private void generateRoleTargets() { + List targetOidList = generatedResources.stream() + .map(r -> r.oid) + .collect(Collectors.toList()); + VelocityGenerator.generate(ROLE_TARGETS_TEMPLATE_FILE, ROLE_TARGETS.file, + Map.of("oidList", targetOidList)); + } + + List getGeneratedResources() { + return generatedResources; + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/TestSystemPerformance.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/TestSystemPerformance.java new file mode 100644 index 00000000000..6ecf85a4899 --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/TestSystemPerformance.java @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import static com.evolveum.midpoint.util.MiscUtil.emptyIfNull; + +import static org.assertj.core.api.Assertions.assertThat; + +import static com.evolveum.midpoint.test.util.MidPointTestConstants.TARGET_DIR_PATH; +import static com.evolveum.midpoint.tools.testng.TestMonitor.PERF_REPORT_PREFIX_PROPERTY_NAME; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import com.evolveum.midpoint.test.util.TestReportUtil; + +import org.apache.commons.collections4.ListUtils; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.testng.annotations.Test; + +import com.evolveum.icf.dummy.resource.DummyAccount; +import com.evolveum.midpoint.prism.PrismContainerDefinition; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismObjectDefinition; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.schema.internals.InternalsConfig; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.task.TaskOperationStatsUtil; +import com.evolveum.midpoint.schema.util.task.TaskPerformanceInformation; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.DummyAuditService; +import com.evolveum.midpoint.test.DummyTestResource; +import com.evolveum.midpoint.test.TestResource; +import com.evolveum.midpoint.test.util.MidPointTestConstants; +import com.evolveum.midpoint.testing.story.AbstractStoryTest; +import com.evolveum.midpoint.tools.testng.PerformanceTestClassMixin; +import com.evolveum.midpoint.tools.testng.TestReportSection; +import com.evolveum.midpoint.util.exception.SystemException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + +/** + * Tests the overall system performance. Can be parameterized using Java properties. + * + * Examples: + * + * -Dsources.resources=2 -Dtargets.resources=3 -Droles.business.count=10 -Droles.technical.count=20 -Droles.assignments.count=4 -Droles.inducements.count=5 + */ +@SuppressWarnings("BusyWait") +@ContextConfiguration(locations = { "classpath:ctx-story-test-main.xml" }) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class TestSystemPerformance extends AbstractStoryTest implements PerformanceTestClassMixin { + + public static final File TEST_DIR = new File(MidPointTestConstants.TEST_RESOURCES_DIR, "system-perf"); + static final File TARGET_DIR = new File(TARGET_DIR_PATH); + + private static final File SYSTEM_CONFIGURATION_FILE = new File(TEST_DIR, "system-configuration.xml"); + + static final SchemaConfiguration SCHEMA_CONFIGURATION; + static final SourcesConfiguration SOURCES_CONFIGURATION; + static final TargetsConfiguration TARGETS_CONFIGURATION; + static final RolesConfiguration ROLES_CONFIGURATION; + static final ImportConfiguration IMPORTS_CONFIGURATION; + static final ReconciliationConfiguration RECONCILIATIONS_CONFIGURATION; + static final RecomputationConfiguration RECOMPUTATION_CONFIGURATION; + + static final OtherParameters OTHER_PARAMETERS; + + private static final List RESOURCE_SOURCE_LIST; + private static final List RESOURCE_TARGET_LIST; + + private static final TestResource ARCHETYPE_BASIC_USER = + new TestResource<>(TEST_DIR, "archetype-basic-user.xml", "463e21c5-9959-48e9-bc2a-5356eafb0589"); + + static final TestResource ROLE_TARGETS = new TestResource<>(TARGET_DIR, "generated-role-targets.xml", "3b65aad7-7d6b-412e-bfc7-2cee44d22c32"); + private static final TestResource TEMPLATE_USER = new TestResource<>(TEST_DIR, "template-user.xml", "0c77fde5-4ad5-49ce-8ee9-14f330660d8e"); + + private static final List> BUSINESS_ROLE_LIST; + private static final List> TECHNICAL_ROLE_LIST; + private static final List> TASK_IMPORT_LIST; + private static final List> TASK_RECONCILIATION_LIST; + private static final TestResource TASK_RECOMPUTE; + + static final long START = System.currentTimeMillis(); + + private static final String REPORT_SECTION_SUMMARY_NAME = "summary"; + private static final String REPORT_SECTION_TASK_EXECUTION_NAME = "taskExecution"; + private static final String REPORT_SECTION_TASK_EXECUTION_DENORMALIZED_NAME = "taskExecutionDenormalized"; + + private TestReportSection taskExecutionReportSection; + private TestReportSection taskExecutionDenormalizedReportSection; + + private final ProgressOutputFile progressOutputFile = new ProgressOutputFile(); + private final SummaryOutputFile summaryOutputFile = new SummaryOutputFile(); + private final DetailsOutputFile detailsOutputFile = new DetailsOutputFile(); + + private final List summaryReportHeader = new ArrayList<>(); + private final List summaryReportDataRow = new ArrayList<>(); + + private long lastProgress; + + static { + SCHEMA_CONFIGURATION = SchemaConfiguration.setup(); + SOURCES_CONFIGURATION = SourcesConfiguration.setup(); + TARGETS_CONFIGURATION = TargetsConfiguration.setup(); + ROLES_CONFIGURATION = RolesConfiguration.setup(); + IMPORTS_CONFIGURATION = ImportConfiguration.setup(); + RECONCILIATIONS_CONFIGURATION = ReconciliationConfiguration.setup(); + RECOMPUTATION_CONFIGURATION = RecomputationConfiguration.setup(); + + OTHER_PARAMETERS = OtherParameters.setup(); + + RESOURCE_SOURCE_LIST = SOURCES_CONFIGURATION.getGeneratedResources(); + RESOURCE_TARGET_LIST = TARGETS_CONFIGURATION.getGeneratedResources(); + BUSINESS_ROLE_LIST = ROLES_CONFIGURATION.getGeneratedBusinessRoles(); + TECHNICAL_ROLE_LIST = ROLES_CONFIGURATION.getGeneratedTechnicalRoles(); + TASK_IMPORT_LIST = IMPORTS_CONFIGURATION.getGeneratedTasks(); + TASK_RECONCILIATION_LIST = RECONCILIATIONS_CONFIGURATION.getGeneratedTasks(); + TASK_RECOMPUTE = RECOMPUTATION_CONFIGURATION.getGeneratedTask(); + + System.setProperty(PERF_REPORT_PREFIX_PROPERTY_NAME, createReportFilePrefix()); + } + + private static String createReportFilePrefix() { + return TARGET_DIR_PATH + "/" + START + "-" + OTHER_PARAMETERS.label + "-report"; + } + + public TestSystemPerformance() throws IOException { + } + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + + DummyAuditService.getInstance().setEnabled(false); + + InternalsConfig.turnOffAllChecks(); + + new SourceInitializer(this, RESOURCE_SOURCE_LIST, initTask) + .run(initResult); + + new TargetInitializer(this, RESOURCE_TARGET_LIST, initTask) + .run(initResult); + + repoAdd(ARCHETYPE_BASIC_USER, initResult); + repoAdd(ROLE_TARGETS, initResult); + repoAdd(TEMPLATE_USER, initResult); + + for (TestResource resource : TECHNICAL_ROLE_LIST) { + addObject(resource, initTask, initResult); // creates resource objects + } + + for (TestResource resource : BUSINESS_ROLE_LIST) { + repoAdd(resource, initResult); + } + + createSummaryReportData(); + } + + private void createSummaryReportData() { + + summaryReportHeader.clear(); + summaryReportHeader.addAll( + Arrays.asList( + "label", + "sources", "accounts", "singleValuedInboundMappings", "multiValuedInboundMappings", "attributeValues", + "targets", "singleValuedOutboundMappings", "multiValuedOutboundMappings", + "businessRoles", "technicalRoles", "assignmentsMin", "assignmentsMax", "inducementsMin", "inducementsMax", + "schemaSingleValuedProperties", "schemaMultiValuedProperties", "schemaIndexedPercentage", + "importTaskThreads", + "reconciliationTaskThreads", + "recomputationTaskThreads")); + + summaryReportDataRow.clear(); + summaryReportDataRow.addAll( + Arrays.asList( + OTHER_PARAMETERS.label, + + SOURCES_CONFIGURATION.getNumberOfResources(), + SOURCES_CONFIGURATION.getNumberOfAccounts(), + SOURCES_CONFIGURATION.getSingleValuedMappings(), + SOURCES_CONFIGURATION.getMultiValuedMappings(), + SOURCES_CONFIGURATION.getAttributeValues(), + + TARGETS_CONFIGURATION.getNumberOfResources(), + TARGETS_CONFIGURATION.getSingleValuedMappings(), + TARGETS_CONFIGURATION.getMultiValuedMappings(), + + ROLES_CONFIGURATION.getNumberOfBusinessRoles(), + ROLES_CONFIGURATION.getNumberOfTechnicalRoles(), + ROLES_CONFIGURATION.getNumberOfAssignmentsMin(), + ROLES_CONFIGURATION.getNumberOfAssignmentsMax(), + ROLES_CONFIGURATION.getNumberOfInducementsMin(), + ROLES_CONFIGURATION.getNumberOfInducementsMax(), + + SCHEMA_CONFIGURATION.getSingleValuedProperties(), + SCHEMA_CONFIGURATION.getMultiValuedProperties(), + SCHEMA_CONFIGURATION.getIndexedPercentage(), + + IMPORTS_CONFIGURATION.getThreads(), + RECONCILIATIONS_CONFIGURATION.getThreads(), + RECOMPUTATION_CONFIGURATION.getThreads())); + } + + @Override + protected boolean isAvoidLoggingChange() { + return false; // we want logging from our system config + } + + @Override + protected File getSystemConfigurationFile() { + return SYSTEM_CONFIGURATION_FILE; + } + + @Override + protected void importSystemTasks(OperationResult initResult) { + // nothing here + } + + @Test + public void test000LogStart() { + + testMonitor().addReportSection(REPORT_SECTION_SUMMARY_NAME) + .withColumns(summaryReportHeader.toArray(new String[0])) + .addRow(summaryReportDataRow.toArray()); + + List taskExecutionHeader = Arrays.asList("task", "time", "timePerAccount"); + taskExecutionReportSection = testMonitor().addReportSection(REPORT_SECTION_TASK_EXECUTION_NAME) + .withColumns(taskExecutionHeader.toArray(new String[0])); + + taskExecutionDenormalizedReportSection = testMonitor().addReportSection(REPORT_SECTION_TASK_EXECUTION_DENORMALIZED_NAME) + .withColumns(ListUtils.union(summaryReportHeader, taskExecutionHeader).toArray(new String[0])); + + logger.info("********** STARTED **********\n"); + logger.info("Extension schema: {}", SCHEMA_CONFIGURATION); + logger.info("Sources: {}", SOURCES_CONFIGURATION); + logger.info("Targets: {}", TARGETS_CONFIGURATION); + logger.info("Roles: {}", ROLES_CONFIGURATION); + logger.info("Import: {}", IMPORTS_CONFIGURATION); + logger.info("Reconciliation: {}", RECONCILIATIONS_CONFIGURATION); + logger.info("Recomputation: {}", RECOMPUTATION_CONFIGURATION); + + summaryOutputFile.logStart(); + } + + @Test + public void test001DumpExtensionSchema() { + PrismObjectDefinition userDef = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(UserType.class); + PrismContainerDefinition userExtDef = userDef.getExtensionDefinition(); + System.out.println(userExtDef.debugDump()); + } + + @Test + public void test100Import() throws Exception { + given(); + + Task task = getTestTask(); + OperationResult result = task.getResult(); + + int usersBefore = repositoryService.countObjects(UserType.class, null, null, result); + + String label = "initial-run-of-"; + + for (int taskIndex = 0; taskIndex < TASK_IMPORT_LIST.size(); taskIndex++) { + String importName = "import #" + taskIndex; + + when(importName); + + TestResource taskImport = TASK_IMPORT_LIST.get(taskIndex); + + lastProgress = 0; + addTask(taskImport, result); + waitForTaskFinish(taskImport.oid, false, 0, OTHER_PARAMETERS.taskTimeout, false, 0, + builder -> builder.taskConsumer(task1 -> recordProgress(label, task1))); + + then(importName); + + // Note: after first import the number of users should be constant + int usersAfter = repositoryService.countObjects(UserType.class, null, null, result); + assertThat(usersAfter - usersBefore) + .as("users after " + importName) + .isEqualTo(SOURCES_CONFIGURATION.getNumberOfAccounts()); + + PrismObject taskAfter = assertTask(taskImport.oid, "after") + .display() + .getObject(); + + logTaskFinish(taskAfter, label); + } + + String accountName = SourceInitializer.getAccountName(0); + + DummyAccount account = RESOURCE_SOURCE_LIST.get(0).controller.getDummyResource().getAccountByUsername(accountName); + Set roles = account.getAttributeValues(SourcesConfiguration.A_ROLE, String.class); + displayValue("Roles for " + accountName, roles); + + Set memberships = RESOURCE_TARGET_LIST.stream() + .flatMap(r -> emptyIfNull(getMemberships(accountName, r)).stream()) + .collect(Collectors.toSet()); + displayValue("Memberships for " + accountName, memberships); + + Set technicalRoles = memberships.stream() + .map(this::getTechnicalRoleName) + .collect(Collectors.toSet()); + displayValue("Technical roles for " + accountName, technicalRoles); + + PrismObject user = assertUserAfterByUsername(accountName) + .assertAssignments(roles.size() + 1) // 1. archetype + .assertLinks(SOURCES_CONFIGURATION.getNumberOfResources() + TARGETS_CONFIGURATION.getNumberOfResources(), 0) + .extension() + .assertSize(SOURCES_CONFIGURATION.getSingleValuedMappings() + SOURCES_CONFIGURATION.getMultiValuedMappings()) + .property(ItemPath.create(getSingleValuedPropertyName(0))) + .assertSize(1) + .end() + .property(ItemPath.create(getMultiValuedPropertyName(0))) + .assertSize(SOURCES_CONFIGURATION.getAttributeValues() * SOURCES_CONFIGURATION.getNumberOfResources()) + .end() + .end() + .getObject(); + + // temporarily disabled +// if (TARGETS_CONFIGURATION.getNumberOfResources() > 0) { +// assertThat(user.asObjectable().getRoleMembershipRef().size()) +// .as("# of role membership refs") +// .isEqualTo(roles.size() + technicalRoles.size() + 2); // 1. archetype, 2. role-targets) +// } + } + + private String getTechnicalRoleName(String membership) { + Matcher matcher = Pattern.compile("business-[0-9]+-(technical-[0-9]+)").matcher(membership); + if (matcher.matches()) { + return matcher.group(1); + } else { + return null; + } + } + + private Set getMemberships(String accountName, DummyTestResource r) { + try { + return r.controller.getDummyResource() + .getAccountByUsername(accountName) + .getAttributeValues(TargetsConfiguration.A_MEMBERSHIP, String.class); + } catch (Exception e) { + throw new SystemException(e); + } + } + + @Test + public void test110ImportAgain() throws Exception { + given(); + + Task task = getTestTask(); + OperationResult result = task.getResult(); + + int usersBefore = repositoryService.countObjects(UserType.class, null, null, result); + + for (int retry = 0; retry < IMPORTS_CONFIGURATION.getNoOpRuns(); retry++) { + + String label = String.format("no-op-run-%d-of-", retry + 1); + + for (int taskIndex = 0; taskIndex < TASK_IMPORT_LIST.size(); taskIndex++) { + String importName = String.format("re-import #%d of resource #%d", retry+1, taskIndex); + + when(importName); + + TestResource taskImport = TASK_IMPORT_LIST.get(taskIndex); + + lastProgress = 0; + restartTask(taskImport.oid, result); + Thread.sleep(500); + + waitForTaskFinish(taskImport.oid, false, 0, OTHER_PARAMETERS.taskTimeout, false, 0, + builder -> builder.taskConsumer(task1 -> recordProgress(label, task1))); + + then(importName); + + int usersAfter = repositoryService.countObjects(UserType.class, null, null, result); + assertThat(usersAfter - usersBefore).as("users created").isEqualTo(0); + + PrismObject taskAfter = assertTask(taskImport.oid, "after") + .display() + .getObject(); + + logTaskFinish(taskAfter, label); + } + } + } + + @Test + public void test120Reconciliation() throws Exception { + given(); + + Task task = getTestTask(); + OperationResult result = task.getResult(); + + for (int run = 0; run < RECONCILIATIONS_CONFIGURATION.getRuns(); run++) { + String label = String.format("run-%d-of-", run + 1); + + for (int taskIndex = 0; taskIndex < TASK_RECONCILIATION_LIST.size(); taskIndex++) { + String importName = String.format("reconciliation #%d of resource #%d", run+1, taskIndex); + + when(importName); + + TestResource reconTask = TASK_RECONCILIATION_LIST.get(taskIndex); + + lastProgress = 0; + if (run == 0) { + addTask(reconTask, result); + } else { + restartTask(reconTask.oid, result); + Thread.sleep(500); + } + + waitForTaskFinish(reconTask.oid, false, 0, OTHER_PARAMETERS.taskTimeout, false, 0, + builder -> builder.taskConsumer(task1 -> recordProgress(label, task1))); + + then(importName); + + PrismObject taskAfter = assertTask(reconTask.oid, "after") + .display() + .getObject(); + + logTaskFinish(taskAfter, label); + } + } + } + + @Test + public void test130RecomputeUsers() throws Exception { + given(); + + Task task = getTestTask(); + OperationResult result = task.getResult(); + + when(); + + lastProgress = 0; + addTask(TASK_RECOMPUTE, result); + waitForTaskFinish(TASK_RECOMPUTE.oid, false, 0, OTHER_PARAMETERS.taskTimeout, false, 0, + builder -> builder.taskConsumer(task1 -> recordProgress("", task1))); + + then(); + + PrismObject taskAfter = assertTask(TASK_RECOMPUTE.oid, "after") + .display() + .getObject(); + + logTaskFinish(taskAfter, ""); + } + + @Test + public void test999Finish() { + logFinish(); + } + + private long getExecutionTime(PrismObject taskAfter) { + long start = XmlTypeConverter.toMillis(taskAfter.asObjectable().getLastRunStartTimestamp()); + long end = XmlTypeConverter.toMillis(taskAfter.asObjectable().getLastRunFinishTimestamp()); + return end - start; + } + + @SuppressWarnings("SameParameterValue") + private String getSingleValuedPropertyName(int i) { + return String.format("p-single-%04d", i); + } + + @SuppressWarnings("SameParameterValue") + private String getMultiValuedPropertyName(int i) { + return String.format("p-multi-%04d", i); + } + + private void logTaskFinish(PrismObject taskAfter, String label) { + String desc = label + taskAfter.getName().getOrig(); + + TaskPerformanceInformation performanceInformation = TaskPerformanceInformation.fromTaskTree(taskAfter.asObjectable()); + long executionTime = getExecutionTime(taskAfter); + int executionTimeSeconds = (int) (executionTime / 1000); + int numberOfAccounts = SOURCES_CONFIGURATION.getNumberOfAccounts(); + double timePerAccount = (double) executionTime / (double) numberOfAccounts; + + logger.info("********** FINISHED: {} **********\n", desc); + logger.info(String.format("Task execution time: %,d ms", executionTime)); + logger.info(String.format("Time per account: %,.1f ms", timePerAccount)); + logger.info(TaskOperationStatsUtil.format(taskAfter.asObjectable().getOperationStats())); + logger.info(performanceInformation.debugDump()); + + summaryOutputFile.logTaskFinish(desc, executionTime, timePerAccount); + detailsOutputFile.logTaskFinish(desc, taskAfter.asObjectable(), performanceInformation); + + List dataRow = Arrays.asList(desc, executionTime, timePerAccount); + taskExecutionReportSection + .addRow(dataRow.toArray()); + taskExecutionDenormalizedReportSection + .addRow(ListUtils.union(summaryReportDataRow, dataRow).toArray()); + + TestReportUtil.reportTaskOperationPerformance(testMonitor(), desc, taskAfter.asObjectable(), + numberOfAccounts, executionTimeSeconds); + TestReportUtil.reportTaskRepositoryPerformance(testMonitor(), desc, taskAfter.asObjectable(), + numberOfAccounts, executionTimeSeconds); + TestReportUtil.reportTaskCachesPerformance(testMonitor(), desc, taskAfter.asObjectable()); + TestReportUtil.reportTaskProvisioningStatistics(testMonitor(), desc, taskAfter.asObjectable()); + } + + private void logFinish() { + summaryOutputFile.logFinish(); + } + + private void recordProgress(String label, Task task) { + Long start = task.getLastRunStartTimestamp(); + long progress = task.getProgress(); + + if (start == null || progress == lastProgress) { + return; + } + lastProgress = progress; + + progressOutputFile.recordProgress(label, task); + + OperationStatsType stats = task.getStoredOperationStatsOrClone(); + logger.info("\n{}", TaskOperationStatsUtil.format(stats)); + + TaskPerformanceInformation performanceInformation = TaskPerformanceInformation.fromTaskTree( + task.getRawTaskObjectClone().asObjectable()); + displayDumpable("performance: " + label + task.getName(), performanceInformation); + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/Util.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/Util.java new file mode 100644 index 00000000000..84dc1ef9c87 --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/Util.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +class Util { + + static List createIndexList(int count) { + return IntStream.range(0, count) + .mapToObj(i -> String.format("%04d", i)) + .collect(Collectors.toList()); + } +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/VelocityGenerator.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/VelocityGenerator.java new file mode 100644 index 00000000000..f147966831f --- /dev/null +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/sysperf/VelocityGenerator.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010-2021 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.testing.story.sysperf; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Map; + +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; + +import com.evolveum.midpoint.util.exception.SystemException; + +public class VelocityGenerator { + + public static void generate(File templateFile, File outputFile, Map parameters) { + + VelocityContext ctx = new VelocityContext(); + parameters.forEach(ctx::put); + try { + try (FileWriter writer = new FileWriter(outputFile); + FileReader reader = new FileReader(templateFile)) { + Velocity.evaluate(ctx, writer, "", reader); + } + } catch (IOException e) { + throw new SystemException(e); + } + } +} diff --git a/testing/story/src/test/resources/system-perf/archetype-basic-user.xml b/testing/story/src/test/resources/system-perf/archetype-basic-user.xml new file mode 100644 index 00000000000..6428103fbd2 --- /dev/null +++ b/testing/story/src/test/resources/system-perf/archetype-basic-user.xml @@ -0,0 +1,14 @@ + + + + basic-user + + + + diff --git a/testing/story/src/test/resources/system-perf/resource-source.vm.xml b/testing/story/src/test/resources/system-perf/resource-source.vm.xml new file mode 100644 index 00000000000..dc1082e6aa1 --- /dev/null +++ b/testing/story/src/test/resources/system-perf/resource-source.vm.xml @@ -0,0 +1,194 @@ + + + + + + + + $resourceInstance + + + + + connectorType + com.evolveum.icf.dummy.connector.DummyConnector + + + connectorVersion + 2.0 + + + + + + + $resourceInstance + + + false + false + false + + + + + account + default + Default Account + true + ri:AccountObjectClass + + icfs:name + + + name + + + + + + ArchetypeType + 463e21c5-9959-48e9-bc2a-5356eafb0589 + + + + assignment + + + +#foreach($index in $singleValuedIndexList) + + ri:a-single-$index + + + + + + extension/p-single-$index + + + +#end +#foreach($index in $multiValuedIndexList) + + ri:a-multi-$index + + + + + + extension/p-multi-$index + + + + + + + + +#end +#if($primary) + + ri:role + + + + RoleType + + + polyStringNorm + c:name + + + + + + + auto-business-role + + + + + assignment + + + + + + + + +#end + + + + + + ri:AccountObjectClass + account + true + + + name + + $projection/attributes/icfs:name + + + + + linked + true + + + deleted + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink + + + + unlinked + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#link + + + + unmatched + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#addFocus + + + + + diff --git a/testing/story/src/test/resources/system-perf/resource-target.vm.xml b/testing/story/src/test/resources/system-perf/resource-target.vm.xml new file mode 100644 index 00000000000..a8ac6cf4aad --- /dev/null +++ b/testing/story/src/test/resources/system-perf/resource-target.vm.xml @@ -0,0 +1,169 @@ + + + + + + + + $resourceInstance + + + + + connectorType + com.evolveum.icf.dummy.connector.DummyConnector + + + connectorVersion + 2.0 + + + + + + + $resourceInstance + + + false + false + false + + + + + account + default + Default Account + true + ri:AccountObjectClass + + icfs:name + + + name + + + +#foreach($index in $singleValuedIndexList) + + ri:a-single-$index + + + data + extension/p-single-$index + + + + + + +#end +#foreach($index in $multiValuedIndexList) + + ri:a-multi-$index + + + data + extension/p-multi-$index + + + + + + +#end + + + ri:membership + + + + + ri:group + true + entitlement + group + objectToSubject + ri:members + icfs:name + + + + + + entitlement + group + true + ri:GroupObjectClass + + icfs:name + + + identifier + + + + + + + + + + ri:AccountObjectClass + account + true + + + name + + $projection/attributes/icfs:name + + + + + linked + true + + + deleted + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink + + + + unlinked + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#link + + + + unmatched + + + + diff --git a/testing/story/src/test/resources/system-perf/role-business.vm.xml b/testing/story/src/test/resources/system-perf/role-business.vm.xml new file mode 100644 index 00000000000..ea058401c27 --- /dev/null +++ b/testing/story/src/test/resources/system-perf/role-business.vm.xml @@ -0,0 +1,23 @@ + + + + + $name + +#foreach($inducedOid in $inducedOidList) + + + +#end + diff --git a/testing/story/src/test/resources/system-perf/role-targets.vm.xml b/testing/story/src/test/resources/system-perf/role-targets.vm.xml new file mode 100644 index 00000000000..4acb01a251b --- /dev/null +++ b/testing/story/src/test/resources/system-perf/role-targets.vm.xml @@ -0,0 +1,19 @@ + + + + targets + +#foreach($oid in $oidList) + + + + + +#end + diff --git a/testing/story/src/test/resources/system-perf/role-technical.vm.xml b/testing/story/src/test/resources/system-perf/role-technical.vm.xml new file mode 100644 index 00000000000..c9bbcf57ff9 --- /dev/null +++ b/testing/story/src/test/resources/system-perf/role-technical.vm.xml @@ -0,0 +1,65 @@ + + + + + technical-$index + +#if($resourceOid != "") + + + + entitlement + group + + +#end + + g-$index + +#if($resourceOid != "") + + + + + ri:membership + + + + + + + + ri:group + + + + + entitlement + group + + + + + + + +#end + + diff --git a/testing/story/src/test/resources/system-perf/schema.vm.xsd b/testing/story/src/test/resources/system-perf/schema.vm.xsd new file mode 100644 index 00000000000..e1814dc1738 --- /dev/null +++ b/testing/story/src/test/resources/system-perf/schema.vm.xsd @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +#foreach($item in $itemList) + + + + $item.indexed + + + +#end + + + diff --git a/testing/story/src/test/resources/system-perf/system-configuration.xml b/testing/story/src/test/resources/system-perf/system-configuration.xml new file mode 100644 index 00000000000..d7d12d41d9e --- /dev/null +++ b/testing/story/src/test/resources/system-perf/system-configuration.xml @@ -0,0 +1,98 @@ + + + + + SystemConfiguration + + File Appender + INFO + + OFF + org.hibernate.engine.jdbc.spi.SqlExceptionHelper + + + OFF + org.hibernate.internal.ExceptionMapperStandardImpl + + + OFF + org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl + + + OFF + org.hibernate.engine.jdbc.batch.internal.BatchingBatch + + + %date [%thread] %-5level \(%logger{46}\): %message%n + target/test.log + true + + + + UserType + + + + + true + true + + + true + true + + + true + + + true + + + true + + + + true + + false + 60 + + perCacheAndObjectType + + + SystemConfigurationType + ArchetypeType + ObjectTemplateType + SecurityPolicyType + ValuePolicyType + ResourceType + RoleType + ShadowType + + + + + perCacheAndObjectType + + + + + + + globallyAndLocally + perOperationAndObjectType + + + + + never + never + + diff --git a/testing/story/src/test/resources/system-perf/task-import.vm.xml b/testing/story/src/test/resources/system-perf/task-import.vm.xml new file mode 100644 index 00000000000..3176fd0e311 --- /dev/null +++ b/testing/story/src/test/resources/system-perf/task-import.vm.xml @@ -0,0 +1,36 @@ + + + + + + + + task-import-$index + + + ri:AccountObjectClass + $workerThreads + + + + runnable + + http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/import/handler-3 + + + single + diff --git a/testing/story/src/test/resources/system-perf/task-recomputation.vm.xml b/testing/story/src/test/resources/system-perf/task-recomputation.vm.xml new file mode 100644 index 00000000000..e40e73efc74 --- /dev/null +++ b/testing/story/src/test/resources/system-perf/task-recomputation.vm.xml @@ -0,0 +1,41 @@ + + + + + + + + task-recomputation + + + UserType + + + + name + u- + true + + + + $workerThreads + + + + runnable + + http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/recompute/handler-3 + + single + diff --git a/testing/story/src/test/resources/system-perf/task-reconciliation.vm.xml b/testing/story/src/test/resources/system-perf/task-reconciliation.vm.xml new file mode 100644 index 00000000000..062e18f4bc8 --- /dev/null +++ b/testing/story/src/test/resources/system-perf/task-reconciliation.vm.xml @@ -0,0 +1,36 @@ + + + + + + + + task-reconciliation-$index + + + ri:AccountObjectClass + $workerThreads + + + + runnable + + http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/reconciliation/handler-3 + + + single + diff --git a/testing/story/src/test/resources/system-perf/template-user.xml b/testing/story/src/test/resources/system-perf/template-user.xml new file mode 100644 index 00000000000..9668d3a314b --- /dev/null +++ b/testing/story/src/test/resources/system-perf/template-user.xml @@ -0,0 +1,30 @@ + + + + template-user + + + + + + + + + + + + + + + + + + + + diff --git a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/AbstractUnitTest.java b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/AbstractUnitTest.java index 25ead7f0b59..297299eb8c2 100644 --- a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/AbstractUnitTest.java +++ b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/AbstractUnitTest.java @@ -6,6 +6,8 @@ */ package com.evolveum.midpoint.tools.testng; +import java.lang.reflect.Method; + import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,6 +25,7 @@ public abstract class AbstractUnitTest implements MidpointTestMixin { protected final Logger logger = LoggerFactory.getLogger(getClass()); + // region perf-test support private TestMonitor testMonitor; /** Called only by tests that need it, implements performance mixin interface. */ @@ -41,6 +44,40 @@ public TestMonitor testMonitor() { return testMonitor; } + // see the comment in PerformanceTestMethodMixin for explanation + @BeforeMethod + public void initTestMethodMonitor() { + if (this instanceof PerformanceTestMethodMixin) { + createTestMonitor(); + } + } + + // see the comment in PerformanceTestMethodMixin for explanation + @AfterMethod + public void dumpMethodReport(Method method) { + if (this instanceof PerformanceTestMethodMixin) { + ((PerformanceTestMethodMixin) this).dumpReport( + getClass().getSimpleName() + "#" + method.getName()); + } + } + + // see the comment in PerformanceTestClassMixin for explanation + @BeforeClass + public void initTestClassMonitor() { + if (this instanceof PerformanceTestClassMixin) { + createTestMonitor(); + } + } + + // see the comment in PerformanceTestClassMixin for explanation + @AfterClass + public void dumpClassReport() { + if (this instanceof PerformanceTestClassMixin) { + ((PerformanceTestClassMixin) this).dumpReport(getClass().getSimpleName()); + } + } + // endregion + @BeforeClass public void displayTestClassTitle() { displayTestTitle("Starting TEST CLASS: " + getClass().getName()); diff --git a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/PerformanceTestClassMixin.java b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/PerformanceTestClassMixin.java index 34aef7074f0..8207ad5ac96 100644 --- a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/PerformanceTestClassMixin.java +++ b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/PerformanceTestClassMixin.java @@ -1,29 +1,30 @@ /* - * Copyright (C) 2010-2020 Evolveum and contributors + * Copyright (C) 2010-2021 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.tools.testng; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; - /** * Mixin supporting work with {@link TestMonitor} at the class-scope level * (one test report for all the methods in the test class). * Details of setting of {@link TestMonitor} is up to the class, methods from * {@link PerformanceTestCommonMixin} must be implemented. + * + * [NOTE] + * ==== + * Actual `@Before/AfterClass` methods are implemented in `AbstractUnitTest` + * and `AbstractSpringTest` using `instanceof` check for two reasons: + * + * * If `@AfterClass` is in interface it is executed after all lifecycle methods from the class + * hierarchy - which may happen after the Spring context is destroyed (for integration tests). + * * If mixin interface is on the abstract class the lifecycle methods *are not called at all* + * in the test subclasses, which really sucks. + * + * So currently this is only marker interface used by lifecycle methods in our two top-level + * classes (unit/Spring) and everything works fine. + * ==== */ public interface PerformanceTestClassMixin extends PerformanceTestCommonMixin { - - @BeforeClass - default void initTestMonitor() { - createTestMonitor(); - } - - @AfterClass - default void dumpReport() { - dumpReport(getClass().getSimpleName()); - } } diff --git a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/PerformanceTestMethodMixin.java b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/PerformanceTestMethodMixin.java index 90dd37fa043..6a74808d235 100644 --- a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/PerformanceTestMethodMixin.java +++ b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/PerformanceTestMethodMixin.java @@ -1,31 +1,30 @@ /* - * Copyright (C) 2010-2020 Evolveum and contributors + * Copyright (C) 2010-2021 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.tools.testng; -import java.lang.reflect.Method; - -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; - /** * Mixin supporting work with {@link TestMonitor} at the method-scope level * (one test report for each test method). * Details of setting of {@link TestMonitor} is up to the class, methods from * {@link PerformanceTestCommonMixin} must be implemented. + * + * [NOTE] + * ==== + * Actual `@Before/AfterMethod` methods are implemented in `AbstractUnitTest` + * and `AbstractSpringTest` using `instanceof` check for two reasons: + * + * * If mixin interface is on the abstract class the lifecycle methods *are not called at all* + * in the test subclasses, which really sucks. + * * To use the same strategy as in {@link PerformanceTestClassMixin} which actually has another + * good reason to do this (issue that does not affect the method lifecycle that much). + * + * So currently this is only marker interface used by lifecycle methods in our two top-level + * classes (unit/Spring) and everything works fine. + * ==== */ public interface PerformanceTestMethodMixin extends PerformanceTestCommonMixin { - - @BeforeMethod - default void initTestMonitor() { - createTestMonitor(); - } - - @AfterMethod - default void dumpReport(Method method) { - dumpReport(getClass().getSimpleName() + "#" + method.getName()); - } } diff --git a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestMonitor.java b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestMonitor.java index 92ba1d7ebdd..10e4486f82f 100644 --- a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestMonitor.java +++ b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestMonitor.java @@ -36,7 +36,7 @@ public class TestMonitor { * Name of a system property that specifies file name prefix for report. * If system property is null, report is dumped to standard output. * Specified file name prefix can be absolute or relative from working directory, - * e.g. {@code target/perf-report}. + * e.g. `target/perf-report`. */ public static final String PERF_REPORT_PREFIX_PROPERTY_NAME = "mp.perf.report.prefix"; @@ -48,15 +48,16 @@ public class TestMonitor { /** * Collection of report sections that will be formatted using {@link #dumpReport(String)}. - * which can be extended in two ways: - *
    - *
  • calling {@link #addReportSection(String)} and then filling the
  • - *
+ * This can be extended in one of these ways: + * + * * Directly by using {@link #addReportSection(String)} and then filling the returned + * {@link TestReportSection}. + * * Using {@link #addReportCallback(ReportCallback)} which can be registered beforehand. */ private final List reportSections = new ArrayList<>(); /** - * + * Callbacks that are called during the report dump, see {@link #addReportCallback)}. */ private final List reportCallbacks = new ArrayList<>(); @@ -95,6 +96,12 @@ public Split stopwatchStart(String name, String description) { return stopwatch(name, description).start(); } + /** + * This registers the callback that will be executed during the report dump and can be used + * to add new sections. + * The advantage of callback is that it can be prepared during the initialization of the test + * monitor without the need to change the point where the dump occurs (some `@After...` method). + */ public TestMonitor addReportCallback(ReportCallback reportCallback) { reportCallbacks.add(reportCallback); return this; @@ -122,7 +129,7 @@ public void dumpReport(String testName) { new FileOutputStream(filename)))) { dumpReport(reportMetadata, out); } catch (FileNotFoundException e) { - System.out.println("Creating report file failed with: " + e.toString()); + System.out.println("Creating report file failed with: " + e); System.out.println("Falling back to stdout dump:"); dumpReportToStdout(reportMetadata); }