From 4845cac3fddda34d89d9b35c077c5f66401690c5 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Fri, 24 Apr 2020 14:37:25 +0200 Subject: [PATCH 1/6] Add first link-related tests (MID-6109) Created experimental midpoint.findAssignee/findAssignees methods to make inter-object links navigation easier. Also introduced experimental AbstractEmptyModelIntegrationTest. Cleaning up display() calls when asserters are created. (We decided to call display() automatically only when creating asserters in known contexts like "before" and "after" some operation.) --- .../impl/controller/CollectionProcessor.java | 29 ++-- .../impl/expr/MidpointFunctionsImpl.java | 48 ++++++ .../model/impl/lens/ClockworkMedic.java | 7 +- ...bstractConfiguredModelIntegrationTest.java | 54 +----- .../AbstractEmptyModelIntegrationTest.java | 91 ++++++++++ .../model/intest/TestCustomRelations.java | 76 +++++++++ .../model/intest/TestLinkedObjects.java | 159 ++++++++++++++++++ .../intest/util/CheckingProgressListener.java | 10 +- .../common/system-configuration-empty.xml | 21 +++ .../custom-relations/system-configuration.xml | 21 +++ .../custom-relations/user-abraham.xml | 11 ++ .../resources/custom-relations/user-isaac.xml | 11 ++ .../test/resources/linked/archetype-token.xml | 57 +++++++ .../resources/linked/service-medallion.xml | 14 ++ .../src/test/resources/linked/user-cavin.xml | 12 ++ .../src/test/resources/linked/user-grammi.xml | 12 ++ .../src/test/resources/linked/user-gruffy.xml | 12 ++ .../src/test/resources/linked/user-zummi.xml | 12 ++ .../model-intest/testng-integration-full.xml | 1 + .../test/AbstractModelIntegrationTest.java | 96 +++++------ .../evolveum/midpoint/test/TestResource.java | 4 + 21 files changed, 622 insertions(+), 136 deletions(-) create mode 100644 model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractEmptyModelIntegrationTest.java create mode 100644 model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestCustomRelations.java create mode 100644 model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestLinkedObjects.java create mode 100644 model/model-intest/src/test/resources/common/system-configuration-empty.xml create mode 100644 model/model-intest/src/test/resources/custom-relations/system-configuration.xml create mode 100644 model/model-intest/src/test/resources/custom-relations/user-abraham.xml create mode 100644 model/model-intest/src/test/resources/custom-relations/user-isaac.xml create mode 100644 model/model-intest/src/test/resources/linked/archetype-token.xml create mode 100644 model/model-intest/src/test/resources/linked/service-medallion.xml create mode 100644 model/model-intest/src/test/resources/linked/user-cavin.xml create mode 100644 model/model-intest/src/test/resources/linked/user-grammi.xml create mode 100644 model/model-intest/src/test/resources/linked/user-gruffy.xml create mode 100644 model/model-intest/src/test/resources/linked/user-zummi.xml diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/CollectionProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/CollectionProcessor.java index 50d4aca3545..2acf6bf6741 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/CollectionProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/CollectionProcessor.java @@ -158,10 +158,10 @@ private boolean isThresholdTriggered(CollectionStats stats, PrismObject warterMarkCount) { - LOGGER.trace("Rule NOT triggered on {} because high watermark count exceeded (watermark: {}, actual: {})", collection, warterMarkCount, stats.getObjectCount()); + Integer waterMarkCount = highWaterMark.getCount(); + if (waterMarkCount != null) { + if (stats.getObjectCount() > waterMarkCount) { + LOGGER.trace("Rule NOT triggered on {} because high watermark count exceeded (watermark: {}, actual: {})", collection, waterMarkCount, stats.getObjectCount()); return false; } } @@ -178,16 +178,16 @@ private boolean isThresholdTriggered(CollectionStats stats, PrismObject ResourceAttributeDefinition getAttributeDefinition(PrismObject T findAssignee(Class type) throws CommunicationException, ObjectNotFoundException, + SchemaException, SecurityViolationException, ConfigurationException, ExpressionEvaluationException { + return MiscUtil.extractSingleton(findAssignees(type), () -> new IllegalStateException("More than one assignee found")); + } + + @Experimental + public List findAssignees(Class type) throws CommunicationException, + ObjectNotFoundException, SchemaException, SecurityViolationException, ConfigurationException, + ExpressionEvaluationException { + ObjectQuery query = prismContext.queryFor(type) + .item(AssignmentHolderType.F_ROLE_MEMBERSHIP_REF) + .ref(getFocusObjectReference().asReferenceValue().clone()) + .build(); + return searchObjects(type, query, null); + } + + @Experimental + @NotNull + private ObjectReferenceType getFocusObjectReference() { + ObjectType focusObject = getFocusObjectAny(); + String oid = focusObject.getOid(); + if (oid == null) { + throw new IllegalStateException("No OID in focus object"); + } + return ObjectTypeUtil.createObjectRef(focusObject, prismContext); + } + + @Experimental + @NotNull + private T getFocusObjectAny() { + LensContext lensContext = (LensContext) getModelContext(); + if (lensContext == null) { + throw new IllegalStateException("No model context present. Are you calling this method within model operation?"); + } + LensFocusContext focusContext = lensContext.getFocusContext(); + if (focusContext == null) { + throw new IllegalStateException("No focus context present"); + } + PrismObject object = focusContext.getObjectAny(); + if (object == null) { + throw new IllegalStateException("No old, current, nor new object in focus context"); + } + return object.asObjectable(); + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkMedic.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkMedic.java index aaef8cbbc39..d83c56bb489 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkMedic.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkMedic.java @@ -208,9 +208,10 @@ public boolean partialExecute(String componentName, ProjectorProcessor processor private boolean shouldExecute(String componentName, ProjectorProcessor processor, LensContext context, LensProjectionContext projectionContext) throws SchemaException { ProcessorExecution processorExecution = processor.getClass().getAnnotation(ProcessorExecution.class); - return processorExecution == null || focusPresenceAndTypeCheckPasses(componentName, context, processorExecution) - && focusDeletionCheckPasses(componentName, context.getFocusContext(), processorExecution) - && projectionDeletionCheckPasses(componentName, projectionContext, processorExecution); + return processorExecution == null || + focusPresenceAndTypeCheckPasses(componentName, context, processorExecution) + && focusDeletionCheckPasses(componentName, context.getFocusContext(), processorExecution) + && projectionDeletionCheckPasses(componentName, projectionContext, processorExecution); } private boolean focusPresenceAndTypeCheckPasses(String componentName, LensContext context, diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java index 5f4286253b1..a8e1c2a6adb 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java @@ -9,22 +9,17 @@ import static org.testng.AssertJUnit.assertNotNull; import com.evolveum.midpoint.model.api.context.EvaluatedAssignmentTarget; -import com.evolveum.midpoint.model.test.AbstractModelIntegrationTest; import com.evolveum.midpoint.prism.PrismContainer; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismProperty; import com.evolveum.midpoint.prism.PrismReferenceValue; -import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.delta.ReferenceDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.path.ItemName; import com.evolveum.midpoint.prism.schema.PrismSchema; -import com.evolveum.midpoint.provisioning.ucf.impl.builtin.ManualConnectorInstance; import com.evolveum.midpoint.schema.constants.MidPointConstants; import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.schema.internals.InternalsConfig; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.DummyResourceContoller; import com.evolveum.midpoint.test.IntegrationTestTools; import com.evolveum.midpoint.test.util.TestUtil; @@ -36,7 +31,6 @@ import org.testng.AssertJUnit; import java.io.File; -import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -44,16 +38,13 @@ import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; -public class AbstractConfiguredModelIntegrationTest extends AbstractModelIntegrationTest { +public class AbstractConfiguredModelIntegrationTest extends AbstractEmptyModelIntegrationTest { public static final File SYSTEM_CONFIGURATION_FILE = new File(COMMON_DIR, "system-configuration.xml"); public static final String SYSTEM_CONFIGURATION_OID = SystemObjectsType.SYSTEM_CONFIGURATION.value(); protected static final int NUMBER_OF_GLOBAL_POLICY_RULES = 7; - public static final File USER_ADMINISTRATOR_FILE = new File(COMMON_DIR, "user-administrator.xml"); - protected static final String USER_ADMINISTRATOR_OID = "00000000-0000-0000-0000-000000000002"; - protected static final String USER_TEMPLATE_FILENAME = COMMON_DIR + "/user-template.xml"; protected static final String USER_TEMPLATE_OID = "10000000-0000-0000-0000-000000000002"; @@ -179,9 +170,6 @@ public class AbstractConfiguredModelIntegrationTest extends AbstractModelIntegra protected static final String RESOURCE_DUMMY_FAKE_FILENAME = COMMON_DIR + "/resource-dummy-fake.xml"; protected static final String RESOURCE_DUMMY_FAKE_OID = "10000000-0000-0000-0000-00000000000f"; - public static final File ROLE_SUPERUSER_FILE = new File(COMMON_DIR, "role-superuser.xml"); - protected static final String ROLE_SUPERUSER_OID = "00000000-0000-0000-0000-000000000004"; - protected static final File ROLE_PIRATE_FILE = new File(COMMON_DIR, "role-pirate.xml"); protected static final String ROLE_PIRATE_OID = "12345678-d34d-b33f-f00d-555555556666"; protected static final String ROLE_PIRATE_NAME = "Pirate"; @@ -552,39 +540,7 @@ public AbstractConfiguredModelIntegrationTest() { super(); } - @Override - public void initSystem(Task initTask, OperationResult initResult) throws Exception { - logger.trace("initSystem"); - - // We want logging config from logback-test.xml and not from system config object (unless suppressed) - InternalsConfig.setAvoidLoggingChange(isAvoidLoggingChange()); - super.initSystem(initTask, initResult); - - modelService.postInit(initResult); - ManualConnectorInstance.setRandomDelayRange(0); - - // System Configuration - PrismObject configuration; - try { - File systemConfigurationFile = getSystemConfigurationFile(); - if (systemConfigurationFile != null) { - configuration = repoAddObjectFromFile(systemConfigurationFile, initResult); - } else { - configuration = addSystemConfigurationObject(initResult); - } - } catch (ObjectAlreadyExistsException e) { - throw new ObjectAlreadyExistsException("System configuration already exists in repository;" + - "looks like the previous test haven't cleaned it up", e); - } - if (configuration != null) { - relationRegistry.applyRelationsConfiguration(configuration.asObjectable()); - } - - // Users - userAdministrator = repoAddObjectFromFile(USER_ADMINISTRATOR_FILE, UserType.class, initResult); - repoAddObjectFromFile(ROLE_SUPERUSER_FILE, initResult); - login(userAdministrator); - } + // initSystem is the same as in the superclass protected int getNumberOfUsers() { return 1; // Administrator @@ -598,12 +554,6 @@ protected File getSystemConfigurationFile() { return SYSTEM_CONFIGURATION_FILE; } - // to be used in very specific cases only (it is invoked when getSystemConfigurationFile returns null). - protected PrismObject addSystemConfigurationObject(OperationResult initResult) throws IOException, CommonException, - EncryptionException { - return null; - } - protected PrismObject getDefaultActor() { return userAdministrator; } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractEmptyModelIntegrationTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractEmptyModelIntegrationTest.java new file mode 100644 index 00000000000..c4cd27b79dc --- /dev/null +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractEmptyModelIntegrationTest.java @@ -0,0 +1,91 @@ +/* + * 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.model.intest; + +import com.evolveum.midpoint.model.test.AbstractModelIntegrationTest; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.crypto.EncryptionException; +import com.evolveum.midpoint.provisioning.ucf.impl.builtin.ManualConnectorInstance; +import com.evolveum.midpoint.schema.internals.InternalsConfig; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.annotation.Experimental; +import com.evolveum.midpoint.util.exception.CommonException; +import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; + +import java.io.File; +import java.io.IOException; + +/** + * Creates empty but functional environment for model integration tests - but without all the configured objects + * in {@link AbstractConfiguredModelIntegrationTest}. + * + * Creates a repo with: + * - empty system configuration + * - administrator user (from common dir) + * - superuser role (from common dir) + */ +@Experimental +public abstract class AbstractEmptyModelIntegrationTest extends AbstractModelIntegrationTest { + + private static final File SYSTEM_CONFIGURATION_EMPTY_FILE = new File(COMMON_DIR, "system-configuration-empty.xml"); + + static final File USER_ADMINISTRATOR_FILE = new File(COMMON_DIR, "user-administrator.xml"); + protected static final String USER_ADMINISTRATOR_OID = "00000000-0000-0000-0000-000000000002"; + + static final File ROLE_SUPERUSER_FILE = new File(COMMON_DIR, "role-superuser.xml"); + protected static final String ROLE_SUPERUSER_OID = "00000000-0000-0000-0000-000000000004"; + + protected PrismObject userAdministrator; + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + logger.trace("initSystem"); + + // We want logging config from logback-test.xml and not from system config object (unless suppressed) + InternalsConfig.setAvoidLoggingChange(isAvoidLoggingChange()); + super.initSystem(initTask, initResult); + + modelService.postInit(initResult); + ManualConnectorInstance.setRandomDelayRange(0); + + // System Configuration + PrismObject configuration; + try { + File systemConfigurationFile = getSystemConfigurationFile(); + if (systemConfigurationFile != null) { + configuration = repoAddObjectFromFile(systemConfigurationFile, initResult); + } else { + configuration = addSystemConfigurationObject(initResult); + } + } catch (ObjectAlreadyExistsException e) { + throw new ObjectAlreadyExistsException("System configuration already exists in repository;" + + "looks like the previous test haven't cleaned it up", e); + } + if (configuration != null) { + relationRegistry.applyRelationsConfiguration(configuration.asObjectable()); + } + + // Users + userAdministrator = repoAddObjectFromFile(USER_ADMINISTRATOR_FILE, UserType.class, initResult); + repoAddObjectFromFile(ROLE_SUPERUSER_FILE, initResult); + login(userAdministrator); + } + + protected File getSystemConfigurationFile() { + return SYSTEM_CONFIGURATION_EMPTY_FILE; + } + + // to be used in very specific cases only (it is invoked when getSystemConfigurationFile returns null). + protected PrismObject addSystemConfigurationObject(OperationResult initResult) + throws IOException, CommonException, EncryptionException { + return null; + } +} diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestCustomRelations.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestCustomRelations.java new file mode 100644 index 00000000000..c1147c26ea0 --- /dev/null +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestCustomRelations.java @@ -0,0 +1,76 @@ +/* + * 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.model.intest; + +import java.io.File; +import javax.xml.namespace.QName; + +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.ContextConfiguration; +import org.testng.annotations.Test; + +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.TestResource; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; + +/** + * Tests behavior of custom (non-standard) relations. + * Currently not part of automated test suites. + */ +@ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"}) +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +public class TestCustomRelations extends AbstractEmptyModelIntegrationTest { + + public static final File TEST_DIR = new File("src/test/resources/custom-relations"); + + private static final File SYSTEM_CONFIGURATION_FILE = new File(TEST_DIR, "system-configuration.xml"); + + private static final TestResource USER_ABRAHAM = new TestResource<>(TEST_DIR, "user-abraham.xml", "855e276c-65d4-49ac-98eb-e871b737bd84"); + private static final TestResource USER_ISAAC = new TestResource<>(TEST_DIR, "user-isaac.xml", "020a0294-7fb7-4d43-845f-dfcd8843bec0"); + + private static final String NS_CUSTOM = "http://custom"; + private static final QName CHILD = new QName(NS_CUSTOM, "child"); + + @Override + public void initSystem(Task initTask, OperationResult initResult) + throws Exception { + super.initSystem(initTask, initResult); + + repoAdd(USER_ISAAC, initResult); + repoAdd(USER_ABRAHAM, initResult); + } + + @Override + protected File getSystemConfigurationFile() { + return SYSTEM_CONFIGURATION_FILE; + } + + @Test(enabled = false) // We do not support user-user relations other than delegation one. + public void test100AddParent() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + + when(); + // @formatter:off + ObjectDelta delta = deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT) + .add(ObjectTypeUtil.createAssignmentTo(USER_ABRAHAM.object, CHILD)) + .asObjectDelta(USER_ISAAC.oid); + // @formatter:on + + executeChanges(delta, null, task, result); + + then(); + assertUser(USER_ISAAC.oid, "after") + .display(); + } +} diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestLinkedObjects.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestLinkedObjects.java new file mode 100644 index 00000000000..1d9d1ae699e --- /dev/null +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestLinkedObjects.java @@ -0,0 +1,159 @@ +/* + * 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.model.intest; + +import static com.evolveum.midpoint.util.MiscUtil.extractSingleton; + +import java.io.File; + +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.ContextConfiguration; +import org.testng.annotations.Test; + +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.TestResource; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ArchetypeType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ServiceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; + +/** + * Various tests related to navigation the links between objects. + * See also https://wiki.evolveum.com/display/midPoint/Linked+objects. + */ +@ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"}) +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +public class TestLinkedObjects extends AbstractEmptyModelIntegrationTest { + + public static final File TEST_DIR = new File("src/test/resources/linked"); + + private static final TestResource ARCHETYPE_TOKEN = new TestResource<>(TEST_DIR, "archetype-token.xml", "e7bff8d1-cebd-4fbe-b935-64cfc2f22f52"); + private static final TestResource SERVICE_MEDALLION = new TestResource<>(TEST_DIR, "service-medallion.xml", "8734f795-f6b4-4cc5-843b-6307aaf88f9d"); + private static final TestResource USER_CAVIN = new TestResource<>(TEST_DIR, "user-cavin.xml", "04753be2-f0f1-4292-8f24-48b0eedfcce3"); + private static final TestResource USER_ZUMMI = new TestResource<>(TEST_DIR, "user-zummi.xml", "3224fccd-27fa-45b5-8cf3-497a0d2dd892"); + private static final TestResource USER_GRUFFY = new TestResource<>(TEST_DIR, "user-gruffy.xml", "30b59b40-2875-410d-8731-482743eb6de2"); + private static final TestResource USER_GRAMMI = new TestResource<>(TEST_DIR, "user-grammi.xml", "041d0c03-c322-4e0d-89ba-a2d49b732674"); + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + + addObject(ARCHETYPE_TOKEN, initTask, initResult); + addObject(SERVICE_MEDALLION, initTask, initResult); + + addObject(USER_CAVIN, initTask, initResult); + repoAdd(USER_ZUMMI, initResult); + repoAdd(USER_GRUFFY, initResult); + repoAdd(USER_GRAMMI, initResult); + } + + @Test + public void test000Sanity() throws Exception { + assertUser(USER_CAVIN.oid, "after init") + .assertOrganizationalUnits() + .display(); + assertService(SERVICE_MEDALLION.oid, "after init") + .assertDescription("Not held") + .display(); + } + + /** + * Cavin's grandfather gives medallion to him. + * We should observe correct data on both the medallion and its holder. + */ + @Test + public void test100GiveMedallionToCavin() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + + when(); + // @formatter:off + ObjectDelta delta = deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT) + .add(ObjectTypeUtil.createAssignmentTo(SERVICE_MEDALLION.object, SchemaConstants.ORG_DEFAULT)) + .asObjectDelta(USER_CAVIN.oid); + // @formatter:on + + executeChanges(delta, null, task, result); + + then(); + assertUserAfter(USER_CAVIN.oid) + .assertOrganizationalUnits("medallion holders"); + + recomputeFocus(ServiceType.class, SERVICE_MEDALLION.oid, task, result); // should occur automatically + assertServiceAfter(SERVICE_MEDALLION.oid) + .assertDescription("Held by cavin (Cavin)"); + } + + /** + * Cavin passes the medallion to Zummi. + * Phase one is that it is unassigned from Cavin. + */ + @Test + public void test110PassMedallionToZummiPhaseOne() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + + refresh(USER_CAVIN, task, result); + AssignmentType medallionAssignment = extractSingleton(USER_CAVIN.getObjectable().getAssignment()); + + when(); + // @formatter:off + ObjectDelta delta = deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT) + .delete(medallionAssignment.clone()) + .asObjectDelta(USER_CAVIN.oid); + // @formatter:on + + executeChanges(delta, null, task, result); + + then(); + assertUserAfter(USER_CAVIN.oid) + .assertOrganizationalUnits(); + + recomputeFocus(ServiceType.class, SERVICE_MEDALLION.oid, task, result); // should occur automatically + assertServiceAfter(SERVICE_MEDALLION.oid) + .assertDescription("Not held"); + } + + /** + * Cavin passes the medallion to Zummi. + * Phase two is that it is assigned to Zummi. + */ + @Test + public void test120PassMedallionToZummiPhaseTwo() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + + when(); + // @formatter:off + ObjectDelta delta = deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT) + .add(ObjectTypeUtil.createAssignmentTo(SERVICE_MEDALLION.object, SchemaConstants.ORG_DEFAULT)) + .asObjectDelta(USER_ZUMMI.oid); + // @formatter:on + + executeChanges(delta, null, task, result); + + then(); + assertUserAfter(USER_ZUMMI.oid) + .assertOrganizationalUnits("medallion holders"); + + recomputeFocus(ServiceType.class, SERVICE_MEDALLION.oid, task, result); // should occur automatically + assertServiceAfter(SERVICE_MEDALLION.oid) + .assertDescription("Held by zummi (Zummi Gummi)"); + } + +} diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/util/CheckingProgressListener.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/util/CheckingProgressListener.java index 816b2d0fcdc..c218626fbb0 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/util/CheckingProgressListener.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/util/CheckingProgressListener.java @@ -11,7 +11,6 @@ import com.evolveum.midpoint.model.api.context.ModelContext; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.model.impl.lens.LensProjectionContext; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; /** * @author semancik @@ -19,12 +18,9 @@ */ public class CheckingProgressListener implements ProgressListener { - /* (non-Javadoc) - * @see com.evolveum.midpoint.model.api.ProgressListener#onProgressAchieved(com.evolveum.midpoint.model.api.context.ModelContext, com.evolveum.midpoint.model.api.ProgressInformation) - */ @Override public void onProgressAchieved(ModelContext modelContext, ProgressInformation progressInformation) { - LensContext lensContext = (LensContext)modelContext; + LensContext lensContext = (LensContext)modelContext; lensContext.checkConsistence(); for (LensProjectionContext projectionContext: lensContext.getProjectionContexts()) { // MID-3213 @@ -32,12 +28,8 @@ public void onProgressAchieved(ModelContext modelContext, ProgressInformation pr } } - /* (non-Javadoc) - * @see com.evolveum.midpoint.model.api.ProgressListener#isAbortRequested() - */ @Override public boolean isAbortRequested() { return false; } - } diff --git a/model/model-intest/src/test/resources/common/system-configuration-empty.xml b/model/model-intest/src/test/resources/common/system-configuration-empty.xml new file mode 100644 index 00000000000..54969384abd --- /dev/null +++ b/model/model-intest/src/test/resources/common/system-configuration-empty.xml @@ -0,0 +1,21 @@ + + + + + + + SystemConfiguration + diff --git a/model/model-intest/src/test/resources/custom-relations/system-configuration.xml b/model/model-intest/src/test/resources/custom-relations/system-configuration.xml new file mode 100644 index 00000000000..201f4afa4cb --- /dev/null +++ b/model/model-intest/src/test/resources/custom-relations/system-configuration.xml @@ -0,0 +1,21 @@ + + + + + SystemConfiguration + + + + + custom:child + + + + diff --git a/model/model-intest/src/test/resources/custom-relations/user-abraham.xml b/model/model-intest/src/test/resources/custom-relations/user-abraham.xml new file mode 100644 index 00000000000..f3e704b53fa --- /dev/null +++ b/model/model-intest/src/test/resources/custom-relations/user-abraham.xml @@ -0,0 +1,11 @@ + + + + abraham + diff --git a/model/model-intest/src/test/resources/custom-relations/user-isaac.xml b/model/model-intest/src/test/resources/custom-relations/user-isaac.xml new file mode 100644 index 00000000000..810e225b18a --- /dev/null +++ b/model/model-intest/src/test/resources/custom-relations/user-isaac.xml @@ -0,0 +1,11 @@ + + + + isaac + diff --git a/model/model-intest/src/test/resources/linked/archetype-token.xml b/model/model-intest/src/test/resources/linked/archetype-token.xml new file mode 100644 index 00000000000..466910f7ce6 --- /dev/null +++ b/model/model-intest/src/test/resources/linked/archetype-token.xml @@ -0,0 +1,57 @@ + + + + token + A token has some special characteristics: + + 1. It is held by at most single user at given time. + + 2. It gives its holder some properties, e.g. the `organizationalUnit` obtains value of + `T holders` for any token T held. + + 3. On the other hand, it knows who holds it by storing the holder's `name` and `fullName` in + its `description` property. + + + + + strong + + + + + organizationalUnit + + + + 2 + UserType + + + + + strong + + + + + description + + + + 1 + + diff --git a/model/model-intest/src/test/resources/linked/service-medallion.xml b/model/model-intest/src/test/resources/linked/service-medallion.xml new file mode 100644 index 00000000000..7646b068404 --- /dev/null +++ b/model/model-intest/src/test/resources/linked/service-medallion.xml @@ -0,0 +1,14 @@ + + + + medallion + + + + diff --git a/model/model-intest/src/test/resources/linked/user-cavin.xml b/model/model-intest/src/test/resources/linked/user-cavin.xml new file mode 100644 index 00000000000..075965fba79 --- /dev/null +++ b/model/model-intest/src/test/resources/linked/user-cavin.xml @@ -0,0 +1,12 @@ + + + + cavin + Cavin + diff --git a/model/model-intest/src/test/resources/linked/user-grammi.xml b/model/model-intest/src/test/resources/linked/user-grammi.xml new file mode 100644 index 00000000000..c790aae3f1a --- /dev/null +++ b/model/model-intest/src/test/resources/linked/user-grammi.xml @@ -0,0 +1,12 @@ + + + + grammi + Grammi Gummi + diff --git a/model/model-intest/src/test/resources/linked/user-gruffy.xml b/model/model-intest/src/test/resources/linked/user-gruffy.xml new file mode 100644 index 00000000000..411594f57f3 --- /dev/null +++ b/model/model-intest/src/test/resources/linked/user-gruffy.xml @@ -0,0 +1,12 @@ + + + + gruffy + Gruffy Gummi + diff --git a/model/model-intest/src/test/resources/linked/user-zummi.xml b/model/model-intest/src/test/resources/linked/user-zummi.xml new file mode 100644 index 00000000000..92168c30d11 --- /dev/null +++ b/model/model-intest/src/test/resources/linked/user-zummi.xml @@ -0,0 +1,12 @@ + + + + zummi + Zummi Gummi + diff --git a/model/model-intest/testng-integration-full.xml b/model/model-intest/testng-integration-full.xml index 34c57377dbe..4b53fe97af3 100644 --- a/model/model-intest/testng-integration-full.xml +++ b/model/model-intest/testng-integration-full.xml @@ -68,6 +68,7 @@ + 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 c2105115073..077c4edaaeb 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 @@ -1873,7 +1873,6 @@ protected ShadowAsserter assertShadow(String username, PrismObject asserter = ShadowAsserter.forShadow(accounts.get(0), "shadow for username " + username + " on " + resource); initializeAsserter(asserter); - asserter.display(); return asserter; } @@ -3861,11 +3860,14 @@ protected void addObjects(File... files) } } - protected void addObject(TestResource resource, Task task, OperationResult result) + protected PrismObject addObject(TestResource resource, Task task, OperationResult result) throws IOException, ObjectNotFoundException, ConfigurationException, SecurityViolationException, PolicyViolationException, ExpressionEvaluationException, ObjectAlreadyExistsException, CommunicationException, SchemaException { - addObject(resource.file, task, result); + PrismObject parsedObject = addObject(resource.file, task, result); + PrismObject storedObject = modelService.getObject(parsedObject.getCompileTimeClass(), resource.oid, null, task, result); + resource.object = storedObject; + return storedObject; } protected PrismObject repoAddObject(TestResource resource, OperationResult result) @@ -4971,7 +4973,6 @@ protected ModelExecuteOptions nullToEmpty(ModelExecuteOptions options) { } protected void modifyUserAddAccount(String userOid, File accountFile, Task task, OperationResult result) throws SchemaException, IOException, ObjectAlreadyExistsException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException { - //noinspection unchecked Collection> deltas = singleton(createAddAccountDelta(userOid, accountFile)); modelService.executeChanges(deltas, null, task, result); } @@ -5078,21 +5079,18 @@ protected CompiledGuiProfileAsserter assertCompiledGuiProfile(MidPointPrin CompiledGuiProfile compiledGuiProfile = ((GuiProfiledPrincipal) principal).getCompiledGuiProfile(); CompiledGuiProfileAsserter asserter = new CompiledGuiProfileAsserter<>(compiledGuiProfile, null, "in principal " + principal); initializeAsserter(asserter); - asserter.display(); return asserter; } protected CompiledGuiProfileAsserter assertCompiledGuiProfile(CompiledGuiProfile compiledGuiProfile) { CompiledGuiProfileAsserter asserter = new CompiledGuiProfileAsserter<>(compiledGuiProfile, null, null); initializeAsserter(asserter); - asserter.display(); return asserter; } protected EvaluatedPolicyRulesAsserter assertEvaluatedPolicyRules(Collection evaluatedPolicyRules, PrismObject sourceObject) { EvaluatedPolicyRulesAsserter asserter = new EvaluatedPolicyRulesAsserter<>(evaluatedPolicyRules, null, sourceObject.toString()); initializeAsserter(asserter); - asserter.display(); return asserter; } @@ -5145,7 +5143,6 @@ protected RoleSelectionSpecificationAsserter asserter = new RoleSelectionSpecificationAsserter<>(spec, null, "for holder " + targetHolder); initializeAsserter(asserter); - asserter.display(); return asserter; } @@ -5156,7 +5153,6 @@ protected RoleSelectionSpecificationAsserter asserter = new RoleSelectionSpecificationAsserter<>(spec, null, "for holder " + targetHolder + ", role type " + roleType.getSimpleName() + ", order " + order); initializeAsserter(asserter); - asserter.display(); return asserter; } @@ -5775,34 +5771,29 @@ protected PendingOperationType findPendingOperation(PrismObject shad } protected UserAsserter assertUserAfter(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - UserAsserter asserter = assertUser(oid, "after"); - asserter.assertOid(oid); - return asserter; + return assertUser(oid, "after") + .display(); } protected UserAsserter assertUserAfterByUsername(String username) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - UserAsserter asserter = assertUserByUsername(username, "after"); - asserter.display(); - asserter.assertName(username); - return asserter; + return assertUserByUsername(username, "after") + .display(); } protected UserAsserter assertUserBefore(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - UserAsserter asserter = assertUser(oid, "before"); - asserter.assertOid(oid); - return asserter; + return assertUser(oid, "before") + .display(); } protected UserAsserter assertUserBeforeByUsername(String username) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - UserAsserter asserter = assertUserByUsername(username, "before"); - asserter.display(); - asserter.assertName(username); - return asserter; + return assertUserByUsername(username, "before") + .display(); } protected UserAsserter assertUser(String oid, String message) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { PrismObject user = getUser(oid); - return assertUser(user, message); + return assertUser(user, message) + .assertOid(oid); } protected TaskAsserter assertTask(String taskOid, String message) throws SchemaException, ObjectNotFoundException { @@ -5827,7 +5818,6 @@ protected RepoOpAsserter createRepoOpAsserter() { protected UserAsserter assertUser(PrismObject user, String message) { UserAsserter asserter = UserAsserter.forUser(user, message); initializeAsserter(asserter); - asserter.display(); return asserter; } @@ -5836,6 +5826,7 @@ protected UserAsserter assertUserByUsername(String username, String messag assertNotNull("User with username '" + username + "' was not found", user); UserAsserter asserter = UserAsserter.forUser(user, message); initializeAsserter(asserter); + asserter.assertName(username); return asserter; } @@ -5853,10 +5844,8 @@ protected OrgAsserter assertOrg(PrismObject org, String message) } protected OrgAsserter assertOrgAfter(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - OrgAsserter asserter = assertOrg(oid, "after"); - asserter.display(); - asserter.assertOid(oid); - return asserter; + return assertOrg(oid, "after") + .display(); } protected OrgAsserter assertOrgByName(String name, String message) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { @@ -5886,23 +5875,22 @@ protected RoleAsserter assertRoleByName(String name, String message) throw protected RoleAsserter assertRole(PrismObject role, String message) { RoleAsserter asserter = RoleAsserter.forRole(role, message); initializeAsserter(asserter); - asserter.display(); return asserter; } protected RoleAsserter assertRoleBefore(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - RoleAsserter asserter = assertRole(oid, "before"); - return asserter; + return assertRole(oid, "before") + .display(); } protected RoleAsserter assertRoleAfter(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - RoleAsserter asserter = assertRole(oid, "after"); - return asserter; + return assertRole(oid, "after") + .display(); } protected RoleAsserter assertRoleAfterByName(String name) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - RoleAsserter asserter = assertRoleByName(name, "after"); - return asserter; + return assertRoleByName(name, "after") + .display(); } protected FocusAsserter assertService(String oid, String message) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { @@ -5928,7 +5916,8 @@ protected FocusAsserter assertServiceAfterByName(String name) } protected FocusAsserter assertServiceAfter(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - return assertService(oid, "after"); + return assertService(oid, "after") + .display(); } protected void assertNoServiceByName(String name) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { @@ -5954,17 +5943,14 @@ protected CaseAsserter assertCase(CaseType aCase, String message) throws O } protected CaseAsserter assertCaseAfter(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - CaseAsserter asserter = assertCase(oid, "after"); - asserter.display(); - asserter.assertOid(oid); - return asserter; + return assertCase(oid, "after") + .display(); } protected ShadowAsserter assertModelShadow(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { PrismObject repoShadow = getShadowModel(oid); ShadowAsserter asserter = ShadowAsserter.forShadow(repoShadow, "model"); - asserter - .display(); + asserter.display(); return asserter; } @@ -5976,8 +5962,7 @@ protected ShadowAsserter assertModelShadowNoFetch(String oid) throws Objec protected ShadowAsserter assertModelShadowFuture(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { PrismObject repoShadow = getShadowModelFuture(oid); ShadowAsserter asserter = ShadowAsserter.forShadow(repoShadow, "model(future)"); - asserter - .display(); + asserter.display(); return asserter; } @@ -5986,8 +5971,7 @@ protected ShadowAsserter assertModelShadowFutureNoFetch(String oid) throws options.setNoFetch(true); PrismObject repoShadow = getShadowModel(oid, options, true); ShadowAsserter asserter = ShadowAsserter.forShadow(repoShadow, "model(future,noFetch)"); - asserter - .display(); + asserter.display(); return asserter; } @@ -5999,14 +5983,12 @@ protected ResourceAsserter assertResource(String oid, String message) thro protected ResourceAsserter assertResource(PrismObject user, String message) { ResourceAsserter asserter = ResourceAsserter.forResource(user, message); initializeAsserter(asserter); - asserter.display(); return asserter; } protected ResourceAsserter assertResourceAfter(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - ResourceAsserter asserter = assertResource(oid, "after"); - asserter.assertOid(oid); - return asserter; + return assertResource(oid, "after") + .display(); } // Change to PrismObjectDefinitionAsserter later @@ -6017,21 +5999,18 @@ protected PrismContainerDefinitionAsserter asser protected PrismContainerDefinitionAsserter assertContainerDefinition(PrismContainerDefinition containerDef) { PrismContainerDefinitionAsserter asserter = PrismContainerDefinitionAsserter.forContainerDefinition(containerDef); initializeAsserter(asserter); - asserter.display(); return asserter; } protected ModelContextAsserter assertPreviewContext(ModelContext previewContext) { ModelContextAsserter asserter = ModelContextAsserter.forContext(previewContext, "preview context"); initializeAsserter(asserter); - asserter.display(); return asserter; } protected OperationResultAsserter assertOperationResult(OperationResult result) { OperationResultAsserter asserter = OperationResultAsserter.forResult(result); initializeAsserter(asserter); - asserter.display(); return asserter; } @@ -6513,7 +6492,6 @@ protected ArchetypePolicyAsserter assertA ArchetypePolicyType archetypePolicy = modelInteractionService.determineArchetypePolicy(object, result); ArchetypePolicyAsserter asserter = new ArchetypePolicyAsserter<>(archetypePolicy, null, "for " + object); initializeAsserter(asserter); - asserter.display(); return asserter; } @@ -6522,7 +6500,6 @@ protected AssignmentCandidatesSpecificationAsse AssignmentCandidatesSpecification targetSpec = modelInteractionService.determineAssignmentTargetSpecification(object, result); AssignmentCandidatesSpecificationAsserter asserter = new AssignmentCandidatesSpecificationAsserter<>(targetSpec, null, "targets for " + object); initializeAsserter(asserter); - asserter.display(); return asserter; } @@ -6531,7 +6508,6 @@ protected AssignmentCandidatesSpecificationAsserter AssignmentCandidatesSpecification targetSpec = modelInteractionService.determineAssignmentHolderSpecification(object, result); AssignmentCandidatesSpecificationAsserter asserter = new AssignmentCandidatesSpecificationAsserter<>(targetSpec, null, "holders for " + object); initializeAsserter(asserter); - asserter.display(); return asserter; } @@ -6559,4 +6535,10 @@ protected void dumpShadowSituations(String resourceOid, OperationResult result) shadow.getSynchronizationSituation(), Boolean.TRUE.equals(shadow.isProtectedObject()) ? " (protected)" : "")); } } + + protected void refresh(TestResource resource, Task task, OperationResult result) + throws CommunicationException, ObjectNotFoundException, SchemaException, SecurityViolationException, + ConfigurationException, ExpressionEvaluationException { + resource.object = modelService.getObject(resource.object.getCompileTimeClass(), resource.oid, null, task, result); + } } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java index 27060acd216..cc5d9e19bb3 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java @@ -31,4 +31,8 @@ public TestResource(File dir, String fileName, String oid) { public String getNameOrig() { return object.getName().getOrig(); } + + public T getObjectable() { + return object.asObjectable(); + } } From 355acb9ce50baa555d9e29b348faf3df5c99ed4b Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Mon, 27 Apr 2020 16:06:20 +0200 Subject: [PATCH 2/6] Add cache diagnostic features 1) Added "secondary size" for query caches (# of results) 2) Added "dump cache content to log" feature Also other minor changes e.g. renaming MatchingRule.isSupported(type) to supports(type). --- .../configuration/InternalsCachePanel.html | 1 + .../configuration/InternalsCachePanel.java | 137 ++- .../midpoint/prism/match/MatchingRule.java | 2 +- .../prism/impl/match/DefaultMatchingRule.java | 2 +- .../match/DistinguishedNameMatchingRule.java | 2 +- .../ExchangeEmailAddressesMatchingRule.java | 2 +- .../impl/match/MatchingRuleRegistryImpl.java | 46 +- .../match/PolyStringNormMatchingRule.java | 2 +- .../match/PolyStringOrigMatchingRule.java | 2 +- .../match/PolyStringStrictMatchingRule.java | 3 +- .../match/StringIgnoreCaseMatchingRule.java | 2 +- .../prism/impl/match/UuidMatchingRule.java | 2 +- .../prism/impl/match/XmlMatchingRule.java | 2 +- .../schema/expression/ExpressionProfiles.java | 9 +- .../xml/ns/public/common/common-core-3.xsd | 15 + .../com/evolveum/midpoint/util/QNameUtil.java | 5 +- .../caching/AbstractThreadLocalCache.java | 6 + .../model/common/SystemObjectCache.java | 29 +- ...bstractSearchExpressionEvaluatorCache.java | 8 + .../expression/functions/FunctionLibrary.java | 9 +- .../ScriptExpressionEvaluatorFactory.java | 3 +- .../script/ScriptExpressionFactory.java | 102 +- .../common/expression/ExpressionTestUtil.java | 2 +- .../expression/script/AbstractScriptTest.java | 2 +- .../expression/script/TestScriptCaching.java | 2 +- .../TriggerCreatorGlobalState.java | 9 + .../model/impl/lens/ClockworkHookHelper.java | 3 +- .../focus/FocusConstraintsChecker.java | 10 +- .../scripting/actions/ScriptExecutor.java | 5 +- .../src/main/resources/ctx-model.xml | 1 - .../model/impl/expr/TestModelExpressions.java | 6 +- .../report/impl/ReportServiceImpl.java | 881 +++++++++--------- .../ConfiguredConnectorInstanceEntry.java | 7 + .../provisioning/impl/ConnectorManager.java | 9 + .../provisioning/impl/ConstraintsChecker.java | 20 +- .../provisioning/impl/ResourceCache.java | 13 +- .../evolveum/midpoint/repo/api/Cacheable.java | 2 + .../repo/cache/AbstractGlobalCacheValue.java | 21 + .../repo/cache/CacheDispatcherImpl.java | 4 +- .../midpoint/repo/cache/CacheRegistry.java | 6 +- .../repo/cache/GlobalCacheObjectValue.java | 33 +- .../cache/GlobalCacheObjectVersionValue.java | 5 +- .../repo/cache/GlobalCacheQueryValue.java | 34 + .../repo/cache/GlobalObjectCache.java | 12 + .../midpoint/repo/cache/GlobalQueryCache.java | 67 +- .../repo/cache/GlobalVersionCache.java | 10 + .../midpoint/repo/cache/LocalObjectCache.java | 10 + .../midpoint/repo/cache/LocalQueryCache.java | 34 +- .../repo/cache/LocalVersionCache.java | 10 + .../midpoint/repo/cache/QueryKey.java | 10 +- .../midpoint/repo/cache/RepositoryCache.java | 23 +- .../CacheInvalidationPerformanceTest.java | 116 +++ .../src/test/resources/logback-test.xml | 2 +- .../SystemConfigurationCacheableAdapter.java | 5 + .../common/expression/ExpressionFactory.java | 5 + .../midpoint/test/AbstractHigherUnitTest.java | 4 +- .../test/AbstractIntegrationTest.java | 4 +- 57 files changed, 1131 insertions(+), 647 deletions(-) create mode 100644 repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/AbstractGlobalCacheValue.java create mode 100644 repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheQueryValue.java create mode 100644 repo/repo-cache/src/test/java/com/evolveum/midpoint/repo/cache/CacheInvalidationPerformanceTest.java diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.html index fb2b88afa7c..c166236d159 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.html @@ -17,6 +17,7 @@

diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.java index f22563fd55d..9d65637db61 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.java @@ -10,6 +10,8 @@ import com.evolveum.midpoint.util.Holder; import com.evolveum.midpoint.util.KeyValueTreeNode; import com.evolveum.midpoint.util.TreeNode; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.component.AceEditor; import com.evolveum.midpoint.web.component.AjaxButton; import com.evolveum.midpoint.web.security.MidPointApplication; @@ -24,11 +26,16 @@ import java.util.ArrayList; import java.util.List; +import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; + public class InternalsCachePanel extends BasePanel{ private static final long serialVersionUID = 1L; + private static final Trace LOGGER = TraceManager.getTrace(InternalsCachePanel.class); + private static final String ID_CLEAR_CACHES_BUTTON = "clearCaches"; + private static final String ID_DUMP_CONTENT_BUTTON = "dumpContent"; private static final String ID_INFORMATION = "information"; public InternalsCachePanel(String id) { @@ -58,7 +65,7 @@ public void setObject(String object) { informationText.setMode(null); add(informationText); - AjaxButton clearCaches = new AjaxButton(ID_CLEAR_CACHES_BUTTON, createStringResource("InternalsCachePanel.button.clearCaches")) { + add(new AjaxButton(ID_CLEAR_CACHES_BUTTON, createStringResource("InternalsCachePanel.button.clearCaches")) { private static final long serialVersionUID = 1L; @@ -67,54 +74,89 @@ public void onClick(AjaxRequestTarget target) { getPageBase().getCacheDispatcher().dispatchInvalidation(null, null, true, null); target.add(InternalsCachePanel.this); } - }; + }); + + add(new AjaxButton(ID_DUMP_CONTENT_BUTTON, createStringResource("InternalsCachePanel.button.dumpContent")) { + + private static final long serialVersionUID = 1L; + + @Override + public void onClick(AjaxRequestTarget target) { + String cacheInformation = getCacheInformation(); + LOGGER.info("Dumping the content of the caches.\nCurrent counters:\n{}\n", cacheInformation); + MidPointApplication.get().getCacheRegistry().dumpContent(); - add(clearCaches); + getSession().success(getPageBase().getString("InternalsCachePanel.result.dumped")); + target.add(getPageBase()); + } + }); + } + + private static class SizeInformation { + private final int size; + private final int secondarySize; + + private SizeInformation(SingleCacheStateInformationType info) { + size = defaultIfNull(info.getSize(), 0); + secondarySize = defaultIfNull(info.getSecondarySize(), 0); + } + + private SizeInformation(ComponentSizeInformationType info) { + size = defaultIfNull(info.getSize(), 0); + secondarySize = defaultIfNull(info.getSecondarySize(), 0); + } } private String getCacheInformation() { StringBuilder sb = new StringBuilder(); - MidPointApplication midPointApplication = MidPointApplication.get(); - if (midPointApplication != null) { - CachesStateInformationType state = midPointApplication.getCacheRegistry().getStateInformation(); - List> trees = new ArrayList<>(); - for (SingleCacheStateInformationType entry : state.getEntry()) { - KeyValueTreeNode root = new KeyValueTreeNode<>(entry.getName(), entry.getSize()); - trees.add(root); - addComponents(root, entry.getComponent()); - } - Holder maxLabelLength = new Holder<>(0); - Holder maxSize = new Holder<>(0); - trees.forEach(tree -> tree.acceptDepthFirst(node -> { - int labelLength = node.getUserObject().getKey().length() + node.getDepth() * 2; - Integer size = node.getUserObject().getValue(); - if (labelLength > maxLabelLength.getValue()) { - maxLabelLength.setValue(labelLength); - } - if (size != null && size > maxSize.getValue()) { - maxSize.setValue(size); - } - })); - int labelSize = Math.max(maxLabelLength.getValue() + 1, 30); - int sizeSize = Math.max((int) Math.log10(maxSize.getValue()) + 2, 7); - int firstPart = labelSize + 3; - int secondPart = sizeSize + 3; - String headerFormatString = " %-" + labelSize + "s | %" + sizeSize + "s \n"; - String valueFormatString = " %-" + labelSize + "s | %" + sizeSize + "d \n"; - //System.out.println("valueFormatString = " + valueFormatString); - sb.append("\n"); - sb.append(String.format(headerFormatString, "Cache", "Size")); - sb.append(StringUtils.repeat("=", firstPart)).append("+").append(StringUtils.repeat("=", secondPart)).append("\n"); - trees.forEach(tree -> { - tree.acceptDepthFirst(node -> printNode(sb, valueFormatString, node)); - sb.append(StringUtils.repeat("-", firstPart)).append("+").append(StringUtils.repeat("-", secondPart)).append("\n"); - }); + CachesStateInformationType state = MidPointApplication.get().getCacheRegistry().getStateInformation(); + List> trees = new ArrayList<>(); + for (SingleCacheStateInformationType entry : state.getEntry()) { + KeyValueTreeNode root = new KeyValueTreeNode<>(entry.getName(), new SizeInformation(entry)); + trees.add(root); + addComponents(root, entry.getComponent()); } + Holder maxLabelLength = new Holder<>(0); + Holder maxSize = new Holder<>(1); // to avoid issues with log10 + Holder maxSecondarySize = new Holder<>(1); // to avoid issues with log10 + trees.forEach(tree -> tree.acceptDepthFirst(node -> { + int labelLength = node.getUserObject().getKey().length() + node.getDepth() * 2; + int size = node.getUserObject().getValue().size; + int secondarySize = node.getUserObject().getValue().secondarySize; + if (labelLength > maxLabelLength.getValue()) { + maxLabelLength.setValue(labelLength); + } + if (size > maxSize.getValue()) { + maxSize.setValue(size); + } + if (secondarySize > maxSecondarySize.getValue()) { + maxSecondarySize.setValue(secondarySize); + } + })); + int labelSize = Math.max(maxLabelLength.getValue() + 1, 30); + int sizeSize = Math.max((int) Math.log10(maxSize.getValue()) + 2, 7); + int secondarySizeSize = Math.max((int) Math.log10(maxSecondarySize.getValue()) + 2, 8); + int firstPart = labelSize + 3; + int secondPart = sizeSize + 2; + int thirdPart = secondarySizeSize + 3; + String headerFormatString = " %-" + labelSize + "s | %" + sizeSize + "s | %" + secondarySizeSize + "s\n"; + String valueFormatString = " %-" + labelSize + "s | %" + sizeSize + "d | %" + secondarySizeSize + "s\n"; + sb.append("\n"); + sb.append(String.format(headerFormatString, "Cache", "Size", "Sec. size")); + sb.append(StringUtils.repeat("=", firstPart)).append("+") + .append(StringUtils.repeat("=", secondPart)).append("+") + .append(StringUtils.repeat("=", thirdPart)).append("\n"); + trees.forEach(tree -> { + tree.acceptDepthFirst(node -> printNode(sb, valueFormatString, node)); + sb.append(StringUtils.repeat("-", firstPart)).append("+") + .append(StringUtils.repeat("-", secondPart)).append("+") + .append(StringUtils.repeat("-", thirdPart)).append("\n"); + }); return sb.toString(); } - private void printNode(StringBuilder sb, String formatString, TreeNode> node) { - Pair pair = node.getUserObject(); + private void printNode(StringBuilder sb, String formatString, TreeNode> node) { + Pair pair = node.getUserObject(); if (pair != null) { int depth = node.getDepth(); StringBuilder prefix = new StringBuilder(); @@ -124,13 +166,22 @@ private void printNode(StringBuilder sb, String formatString, TreeNode node, List components) { + private void addComponents(KeyValueTreeNode node, List components) { for (ComponentSizeInformationType component : components) { - KeyValueTreeNode child = node.createChild(component.getName(), component.getSize()); + KeyValueTreeNode child = node.createChild(component.getName(), new SizeInformation(component)); addComponents(child, component.getComponent()); } } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/match/MatchingRule.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/match/MatchingRule.java index a5279fa0bde..eb8adecddbd 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/match/MatchingRule.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/match/MatchingRule.java @@ -29,7 +29,7 @@ public interface MatchingRule { /** * Returns true if the rule can be applied to the specified XSD type. */ - boolean isSupported(QName xsdType); + boolean supports(QName xsdType); /** * Matches two objects. diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DefaultMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DefaultMatchingRule.java index f4d6fed21cb..c9f161c8fc2 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DefaultMatchingRule.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DefaultMatchingRule.java @@ -34,7 +34,7 @@ public QName getName() { * @see com.evolveum.midpoint.model.match.MatchingRule#isSupported(java.lang.Class, javax.xml.namespace.QName) */ @Override - public boolean isSupported(QName xsdType) { + public boolean supports(QName xsdType) { // We support everything. We are the default. return true; } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DistinguishedNameMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DistinguishedNameMatchingRule.java index 04bce44cb0e..695ac646394 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DistinguishedNameMatchingRule.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DistinguishedNameMatchingRule.java @@ -34,7 +34,7 @@ public QName getName() { } @Override - public boolean isSupported(QName xsdType) { + public boolean supports(QName xsdType) { return (DOMUtil.XSD_STRING.equals(xsdType)); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/ExchangeEmailAddressesMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/ExchangeEmailAddressesMatchingRule.java index f4dc479a76a..ef56cc019f4 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/ExchangeEmailAddressesMatchingRule.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/ExchangeEmailAddressesMatchingRule.java @@ -29,7 +29,7 @@ public QName getName() { } @Override - public boolean isSupported(QName xsdType) { + public boolean supports(QName xsdType) { return (DOMUtil.XSD_STRING.equals(xsdType)); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/MatchingRuleRegistryImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/MatchingRuleRegistryImpl.java index 9086ca12fc8..af2fa677d8a 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/MatchingRuleRegistryImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/MatchingRuleRegistryImpl.java @@ -23,7 +23,7 @@ public class MatchingRuleRegistryImpl implements MatchingRuleRegistry { @NotNull private final MatchingRule defaultMatchingRule; - @NotNull private final Map> matchingRules = new HashMap<>(); + @NotNull private final Map> matchingRules = new HashMap<>(); MatchingRuleRegistryImpl() { this.defaultMatchingRule = new DefaultMatchingRule<>(); @@ -34,27 +34,39 @@ public class MatchingRuleRegistryImpl implements MatchingRuleRegistry { @NotNull public MatchingRule getMatchingRule(QName ruleName, QName typeQName) throws SchemaException { if (ruleName == null) { + //noinspection unchecked return (MatchingRule) defaultMatchingRule; } - MatchingRule matchingRule = (MatchingRule) matchingRules.get(ruleName); - if (matchingRule == null) { - //try match according to the localPart - if (QNameUtil.matchAny(ruleName, matchingRules.keySet())){ - ruleName = QNameUtil.resolveNs(ruleName, matchingRules.keySet()); - matchingRule = (MatchingRule) matchingRules.get(ruleName); - } - if (matchingRule == null) { - throw new SchemaException("Unknown matching rule for name " + ruleName); - } - } - if (typeQName != null && !matchingRule.isSupported(typeQName)) { - throw new SchemaException("Matching rule "+ruleName+" does not support type "+typeQName); + MatchingRule matchingRule = findMatchingRule(ruleName); + if (typeQName == null || matchingRule.supports(typeQName)) { + return matchingRule; + } else { + throw new SchemaException("Matching rule " + ruleName + " does not support type " + typeQName); } - return matchingRule; } - void registerMatchingRule(MatchingRule rule) { - ((Map)this.matchingRules).put(rule.getName(), rule); + @NotNull + private MatchingRule findMatchingRule(QName ruleName) throws SchemaException { + //noinspection unchecked + MatchingRule rule = (MatchingRule) matchingRules.get(ruleName); + if (rule != null) { + return rule; + } + + // try match according to the localPart + QName qualifiedRuleName = QNameUtil.resolveNs(ruleName, matchingRules.keySet()); + if (qualifiedRuleName != null) { + //noinspection unchecked + MatchingRule ruleAfterQualification = (MatchingRule) matchingRules.get(qualifiedRuleName); + if (ruleAfterQualification != null) { + return ruleAfterQualification; + } + } + + throw new SchemaException("Couldn't find matching rule named '" + ruleName + "'"); } + void registerMatchingRule(MatchingRule rule) { + matchingRules.put(rule.getName(), rule); + } } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringNormMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringNormMatchingRule.java index dd34480f81e..db5b6f3b431 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringNormMatchingRule.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringNormMatchingRule.java @@ -34,7 +34,7 @@ public QName getName() { * @see com.evolveum.midpoint.prism.match.MatchingRule#isSupported(java.lang.Class, javax.xml.namespace.QName) */ @Override - public boolean isSupported(QName xsdType) { + public boolean supports(QName xsdType) { return (PolyStringType.COMPLEX_TYPE.equals(xsdType)); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringOrigMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringOrigMatchingRule.java index e28d387b903..384c105f865 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringOrigMatchingRule.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringOrigMatchingRule.java @@ -34,7 +34,7 @@ public QName getName() { * @see com.evolveum.midpoint.prism.match.MatchingRule#isSupported(java.lang.Class, javax.xml.namespace.QName) */ @Override - public boolean isSupported(QName xsdType) { + public boolean supports(QName xsdType) { return (PolyStringType.COMPLEX_TYPE.equals(xsdType)); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringStrictMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringStrictMatchingRule.java index 767fc0f5def..07876a39936 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringStrictMatchingRule.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringStrictMatchingRule.java @@ -13,7 +13,6 @@ import com.evolveum.midpoint.prism.PrismConstants; import com.evolveum.midpoint.prism.match.MatchingRule; import com.evolveum.midpoint.prism.polystring.PolyString; -import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; /** @@ -34,7 +33,7 @@ public QName getName() { * @see com.evolveum.midpoint.prism.match.MatchingRule#isSupported(java.lang.Class, javax.xml.namespace.QName) */ @Override - public boolean isSupported(QName xsdType) { + public boolean supports(QName xsdType) { return (PolyStringType.COMPLEX_TYPE.equals(xsdType)); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/StringIgnoreCaseMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/StringIgnoreCaseMatchingRule.java index a10dceffc5d..db8765ba503 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/StringIgnoreCaseMatchingRule.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/StringIgnoreCaseMatchingRule.java @@ -31,7 +31,7 @@ public QName getName() { } @Override - public boolean isSupported(QName xsdType) { + public boolean supports(QName xsdType) { return (DOMUtil.XSD_STRING.equals(xsdType)); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/UuidMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/UuidMatchingRule.java index 05a6d9ede61..65a12fbd372 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/UuidMatchingRule.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/UuidMatchingRule.java @@ -33,7 +33,7 @@ public QName getName() { } @Override - public boolean isSupported(QName xsdType) { + public boolean supports(QName xsdType) { return (DOMUtil.XSD_STRING.equals(xsdType)); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/XmlMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/XmlMatchingRule.java index 1ced4e948ca..a5449d2949b 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/XmlMatchingRule.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/XmlMatchingRule.java @@ -37,7 +37,7 @@ public QName getName() { } @Override - public boolean isSupported(QName xsdType) { + public boolean supports(QName xsdType) { return (DOMUtil.XSD_STRING.equals(xsdType)); } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/expression/ExpressionProfiles.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/expression/ExpressionProfiles.java index d3e24eabc6e..8e348f56b71 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/expression/ExpressionProfiles.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/expression/ExpressionProfiles.java @@ -6,8 +6,9 @@ */ package com.evolveum.midpoint.schema.expression; -import java.util.HashMap; +import java.util.Collections; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * @author semancik @@ -15,7 +16,7 @@ */ public class ExpressionProfiles { - private final Map profiles = new HashMap<>(); + private final Map profiles = new ConcurrentHashMap<>(); public ExpressionProfile getProfile(String identifier) { return profiles.get(identifier); @@ -28,4 +29,8 @@ public void add(ExpressionProfile profile) { public int size() { return profiles.size(); } + + public Map getProfiles() { + return Collections.unmodifiableMap(profiles); + } } 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 3d7be668652..32ecd3a5135 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 @@ -26487,6 +26487,14 @@ + + + + Current "secondary size" of the cache, if applicable. This can mean e.g. number of cached objects + for query caches. + + + @@ -26531,6 +26539,13 @@ + + + + Component secondary size (if applicable). + + + diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/QNameUtil.java b/infra/util/src/main/java/com/evolveum/midpoint/util/QNameUtil.java index 702293bfbef..d44f0f000eb 100644 --- a/infra/util/src/main/java/com/evolveum/midpoint/util/QNameUtil.java +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/QNameUtil.java @@ -259,15 +259,14 @@ public static boolean matchWithUri(QName qname, String uri) { return match(qname, uriToQName(uri, true)); } - - public static QName resolveNs(QName a, Collection col){ + public static QName resolveNs(QName a, Collection col) { if (col == null) { return null; } QName found = null; for (QName b: col) { if (match(a, b)) { - if (found != null){ + if (found != null) { throw new IllegalStateException("Found more than one suitable qnames( "+ found + b + ") for attribute: " + a); } found = b; diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java b/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java index 7d1993fa690..bb5f3e258cd 100644 --- a/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java @@ -168,4 +168,10 @@ public static int getTotalSize(ConcurrentHa } protected abstract int getSize(); + + public static void dumpContent(ConcurrentHashMap cacheInstances) { + cacheInstances.forEach((thread, cache) -> cache.dumpContent(thread.getName())); + } + + protected abstract void dumpContent(String threadName); } diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/SystemObjectCache.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/SystemObjectCache.java index 960a24cfaf5..915bddd1b49 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/SystemObjectCache.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/SystemObjectCache.java @@ -10,6 +10,7 @@ import com.evolveum.midpoint.model.common.expression.ExpressionProfileCompiler; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.repo.cache.CacheRegistry; import com.evolveum.midpoint.repo.api.Cacheable; @@ -56,6 +57,7 @@ public class SystemObjectCache implements Cacheable { private static final Trace LOGGER = TraceManager.getTrace(SystemObjectCache.class); + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(SystemObjectCache.class.getName() + ".content"); @Autowired @Qualifier("cacheRepositoryService") @@ -69,7 +71,7 @@ public class SystemObjectCache implements Cacheable { private PrismObject securityPolicy; private Long securityPolicyCheckTimestamp; - private ExpressionProfiles expressionProfiles; + private volatile ExpressionProfiles expressionProfiles; @PostConstruct public void register() { @@ -217,6 +219,7 @@ public synchronized void invalidateCaches() { securityPolicyCheckTimestamp = null; } + @SuppressWarnings("WeakerAccess") public PrismObject getArchetype(String oid, OperationResult result) throws ObjectNotFoundException, SchemaException { // TODO: make this efficient (use cache) return cacheRepositoryService.getObject(ArchetypeType.class, oid, null, result); @@ -262,6 +265,7 @@ public void invalidate(Class type, String oid, CacheInvalidationContext conte if (type == null || type.isAssignableFrom(SystemConfigurationType.class)) { invalidateCaches(); } + expressionProfiles = null; } @NotNull @@ -274,7 +278,28 @@ public Collection getStateInformation() { } private int getSize() { + ExpressionProfiles cachedProfiles = this.expressionProfiles; + return (systemConfiguration != null ? 1 : 0) + - (expressionProfiles != null ? expressionProfiles.size() : 0); + (cachedProfiles != null ? cachedProfiles.size() : 0); + } + + @Override + public void dumpContent() { + if (LOGGER_CONTENT.isInfoEnabled()) { + PrismObject cachedConfiguration = this.systemConfiguration; + if (cachedConfiguration != null) { + LOGGER_CONTENT.info("Cached system configuration: {} (version {}); systemConfigurationCheckTimestamp: {}", + cachedConfiguration, cachedConfiguration.getVersion(), + XmlTypeConverter.createXMLGregorianCalendar(systemConfigurationCheckTimestamp)); + } else { + LOGGER_CONTENT.info("No cached system configuration"); + } + + ExpressionProfiles cachedProfiles = this.expressionProfiles; + if (cachedProfiles != null) { + cachedProfiles.getProfiles().forEach((k, v) -> LOGGER_CONTENT.info("Cached expression profile: {}: {}", k, v)); + } + } } } diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/caching/AbstractSearchExpressionEvaluatorCache.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/caching/AbstractSearchExpressionEvaluatorCache.java index eb5dea20b36..0fe5ac09c99 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/caching/AbstractSearchExpressionEvaluatorCache.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/caching/AbstractSearchExpressionEvaluatorCache.java @@ -41,6 +41,7 @@ public abstract class AbstractSearchExpressionEvaluatorCache LOGGER.info("Cached search expression evaluation [{}] {}: {}", threadName, qk, qr)); + } + } } diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/FunctionLibrary.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/FunctionLibrary.java index 4c3252d99f7..96780701a9e 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/FunctionLibrary.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/FunctionLibrary.java @@ -40,5 +40,12 @@ public void setGenericFunctions(Object genericFunctions) { this.genericFunctions = genericFunctions; } - + @Override + public String toString() { + return "FunctionLibrary{" + + "variableName='" + variableName + '\'' + + ", namespace='" + namespace + '\'' + + ", genericFunctions=" + genericFunctions + + '}'; + } } diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionEvaluatorFactory.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionEvaluatorFactory.java index b64e1b4934b..c84d0870164 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionEvaluatorFactory.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionEvaluatorFactory.java @@ -82,7 +82,8 @@ public ExpressionEvaluator } ScriptExpressionEvaluatorType scriptType = (ScriptExpressionEvaluatorType) evaluatorElementObject; - ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(scriptType, outputDefinition, expressionProfile, factory, contextDescription, task, result); + ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(scriptType, outputDefinition, + expressionProfile, factory, contextDescription, result); return new ScriptExpressionEvaluator(ELEMENT_NAME, scriptType, outputDefinition, protector, prismContext, scriptExpression, securityContextManager, localizationService); diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionFactory.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionFactory.java index 286e43881bd..24aa9e55b97 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionFactory.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionFactory.java @@ -7,6 +7,8 @@ package com.evolveum.midpoint.model.common.expression.script; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -17,7 +19,6 @@ import com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.repo.api.Cacheable; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.repo.cache.CacheRegistry; @@ -34,7 +35,6 @@ import com.evolveum.midpoint.schema.expression.ScriptExpressionProfile; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; -import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SecurityViolationException; import com.evolveum.midpoint.util.logging.Trace; @@ -49,17 +49,18 @@ public class ScriptExpressionFactory implements Cacheable { private static final Trace LOGGER = TraceManager.getTrace(ScriptExpressionFactory.class); + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(ScriptExpressionFactory.class.getName() + ".content"); - public static final String DEFAULT_LANGUAGE = "http://midpoint.evolveum.com/xml/ns/public/expression/language#Groovy"; + private static final String DEFAULT_LANGUAGE = "http://midpoint.evolveum.com/xml/ns/public/expression/language#Groovy"; - private Map evaluatorMap = new HashMap<>(); + private final Map evaluatorMap = new HashMap<>(); private ObjectResolver objectResolver; private final PrismContext prismContext; private Collection functions; - private final Protector protector; private final RepositoryService repositoryService; // might be null during low-level testing - private Map customFunctionLibraryCache; + @NotNull private final Map customFunctionLibraryCache = new ConcurrentHashMap<>(); + private final AtomicBoolean initialized = new AtomicBoolean(false); private CacheRegistry cacheRegistry; @@ -73,9 +74,8 @@ public void unregister() { cacheRegistry.unregisterCacheableService(this); } - public ScriptExpressionFactory(PrismContext prismContext, Protector protector, RepositoryService repositoryService) { + public ScriptExpressionFactory(PrismContext prismContext, RepositoryService repositoryService) { this.prismContext = prismContext; - this.protector = protector; this.repositoryService = repositoryService; } @@ -105,10 +105,6 @@ public Map getEvaluators() { return evaluatorMap; } - public CacheRegistry geCacheRegistry() { - return cacheRegistry; - } - public void setCacheRegistry(CacheRegistry registry) { this.cacheRegistry = registry; } @@ -116,10 +112,10 @@ public void setCacheRegistry(CacheRegistry registry) { public ScriptExpression createScriptExpression( ScriptExpressionEvaluatorType expressionType, ItemDefinition outputDefinition, ExpressionProfile expressionProfile, ExpressionFactory expressionFactory, - String shortDesc, Task task, OperationResult result) + String shortDesc, OperationResult result) throws ExpressionSyntaxException, SecurityViolationException { - initializeCustomFunctionsLibraryCache(expressionFactory, result); + initializeCustomFunctionsLibraryCacheIfNeeded(expressionFactory, result); //cache cleanup method String language = getLanguage(expressionType); @@ -167,39 +163,41 @@ private ScriptExpressionProfile processScriptExpressionProfile(ExpressionProfile return scriptProfile; } - // if performance becomes an issue, replace 'synchronized' with something more elaborate - private synchronized void initializeCustomFunctionsLibraryCache(ExpressionFactory expressionFactory, - OperationResult result) throws ExpressionSyntaxException { - if (customFunctionLibraryCache != null) { - return; + private void initializeCustomFunctionsLibraryCacheIfNeeded(ExpressionFactory expressionFactory, OperationResult result) + throws ExpressionSyntaxException { + if (initialized.compareAndSet(false, true)) { + initializeCustomFunctionsLibraryCache(expressionFactory, result); } - customFunctionLibraryCache = new HashMap<>(); - if (repositoryService == null) { + } + + private void initializeCustomFunctionsLibraryCache(ExpressionFactory expressionFactory, OperationResult result) + throws ExpressionSyntaxException { + if (repositoryService != null) { + OperationResult subResult = result + .createMinorSubresult(ScriptExpressionFactory.class.getName() + ".searchCustomFunctions"); + ResultHandler functionLibraryHandler = (object, parentResult) -> { + // TODO: determine profile from function library archetype + ExpressionProfile expressionProfile = MiscSchemaUtil.getExpressionProfile(); + FunctionLibrary customLibrary = new FunctionLibrary(); + customLibrary.setVariableName(object.getName().getOrig()); + customLibrary.setGenericFunctions( + new CustomFunctions(object.asObjectable(), expressionFactory, expressionProfile)); + customLibrary.setNamespace(MidPointConstants.NS_FUNC_CUSTOM); + customFunctionLibraryCache.put(object.getName().getOrig(), customLibrary); + return true; + }; + try { + repositoryService.searchObjectsIterative(FunctionLibraryType.class, null, functionLibraryHandler, + SelectorOptions.createCollection(GetOperationOptions.createReadOnly()), true, subResult); + subResult.recordSuccessIfUnknown(); + } catch (SchemaException | RuntimeException e) { + subResult.recordFatalError("Failed to initialize custom functions", e); + throw new ExpressionSyntaxException( + "An error occurred during custom libraries initialization. " + e.getMessage(), e); + } + } else { LOGGER.warn("No repository service set for ScriptExpressionFactory; custom functions will not be loaded. This" + " can occur during low-level testing; never during standard system execution."); - return; - } - OperationResult subResult = result - .createMinorSubresult(ScriptExpressionFactory.class.getName() + ".searchCustomFunctions"); - ResultHandler functionLibraryHandler = (object, parentResult) -> { - // TODO: determine profile from function library archetype - ExpressionProfile expressionProfile = MiscSchemaUtil.getExpressionProfile(); - FunctionLibrary customLibrary = new FunctionLibrary(); - customLibrary.setVariableName(object.getName().getOrig()); - customLibrary.setGenericFunctions( - new CustomFunctions(object.asObjectable(), expressionFactory, expressionProfile)); - customLibrary.setNamespace(MidPointConstants.NS_FUNC_CUSTOM); - customFunctionLibraryCache.put(object.getName().getOrig(), customLibrary); - return true; - }; - try { - repositoryService.searchObjectsIterative(FunctionLibraryType.class, null, functionLibraryHandler, - SelectorOptions.createCollection(GetOperationOptions.createReadOnly()), true, subResult); - subResult.recordSuccessIfUnknown(); - } catch (SchemaException | RuntimeException e) { - subResult.recordFatalError("Failed to initialize custom functions", e); - throw new ExpressionSyntaxException( - "An error occurred during custom libraries initialization. " + e.getMessage(), e); } } @@ -229,7 +227,7 @@ private String getLanguage(ScriptExpressionEvaluatorType expressionType) { public void invalidate(Class type, String oid, CacheInvalidationContext context) { if (type == null || type.isAssignableFrom(FunctionLibraryType.class)) { // Currently we don't try to select entries to be cleared based on OID - customFunctionLibraryCache = null; + customFunctionLibraryCache.clear(); } } @@ -238,7 +236,17 @@ public void invalidate(Class type, String oid, CacheInvalidationContext conte public Collection getStateInformation() { return Collections.singleton(new SingleCacheStateInformationType(prismContext) .name(ScriptExpressionFactory.class.getName()) - .size(customFunctionLibraryCache != null ? customFunctionLibraryCache.size() : 0)); + .size(customFunctionLibraryCache.size())); } -} + @Override + public void dumpContent() { + if (LOGGER_CONTENT.isInfoEnabled()) { + if (initialized.get()) { + customFunctionLibraryCache.forEach((k, v) -> LOGGER_CONTENT.info("Cached function library: {}: {}", k, v)); + } else { + LOGGER_CONTENT.info("Custom function library cache is not yet initialized"); + } + } + } +} diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java index aa033fa0a49..f7f98a2e6b0 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java @@ -90,7 +90,7 @@ public static ExpressionFactory createInitializedExpressionFactory(ObjectResolve Collection functions = new ArrayList<>(); functions.add(FunctionLibraryUtil.createBasicFunctionLibrary(prismContext, protector, clock)); functions.add(FunctionLibraryUtil.createLogFunctionLibrary(prismContext)); - ScriptExpressionFactory scriptExpressionFactory = new ScriptExpressionFactory(prismContext, protector, repositoryService); + ScriptExpressionFactory scriptExpressionFactory = new ScriptExpressionFactory(prismContext, repositoryService); scriptExpressionFactory.setObjectResolver(resolver); scriptExpressionFactory.setFunctions(functions); diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java index 24e96436ae4..fad5b31d03f 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java @@ -85,7 +85,7 @@ public void setupFactory() { Clock clock = new Clock(); Collection functions = new ArrayList<>(); functions.add(FunctionLibraryUtil.createBasicFunctionLibrary(prismContext, protector, clock)); - scriptExpressionfactory = new ScriptExpressionFactory(prismContext, protector, null); + scriptExpressionfactory = new ScriptExpressionFactory(prismContext, null); scriptExpressionfactory.setObjectResolver(resolver); scriptExpressionfactory.setFunctions(functions); localizationService = LocalizationTestUtil.getLocalizationService(); diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java index 21fe56a9e2b..719547c0c43 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java @@ -80,7 +80,7 @@ public void setupFactory() { Clock clock = new Clock(); Collection functions = new ArrayList<>(); functions.add(FunctionLibraryUtil.createBasicFunctionLibrary(prismContext, protector, clock)); - scriptExpressionfactory = new ScriptExpressionFactory(prismContext, protector, null); + scriptExpressionfactory = new ScriptExpressionFactory(prismContext, null); scriptExpressionfactory.setObjectResolver(resolver); scriptExpressionfactory.setFunctions(functions); evaluator = new Jsr223ScriptEvaluator("groovy", prismContext, protector, LocalizationTestUtil.getLocalizationService()); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/triggerSetter/TriggerCreatorGlobalState.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/triggerSetter/TriggerCreatorGlobalState.java index 7445cdab95e..1b0cae48c40 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/triggerSetter/TriggerCreatorGlobalState.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/triggerSetter/TriggerCreatorGlobalState.java @@ -8,6 +8,7 @@ package com.evolveum.midpoint.model.impl.expr.triggerSetter; import com.evolveum.midpoint.CacheInvalidationContext; +import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionFactory; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.repo.api.Cacheable; import com.evolveum.midpoint.repo.api.DeleteObjectResult; @@ -36,6 +37,7 @@ public class TriggerCreatorGlobalState implements Cacheable { private static final Trace LOGGER = TraceManager.getTrace(TriggerCreatorGlobalState.class); + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(TriggerCreatorGlobalState.class.getName() + ".content"); private AtomicLong lastExpirationCleanup = new AtomicLong(0L); @@ -107,6 +109,13 @@ public Collection getStateInformation() { ); } + @Override + public void dumpContent() { + if (LOGGER_CONTENT.isInfoEnabled()) { + state.forEach((k, v) -> LOGGER_CONTENT.info("Cached trigger creation: {}: {}", k, v)); + } + } + @PostConstruct public void register() { cacheRegistry.registerCacheableService(this); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkHookHelper.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkHookHelper.java index 42699f046b9..9ed7fd6ab1b 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkHookHelper.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkHookHelper.java @@ -153,7 +153,8 @@ private void evaluateScriptingHook(LensContext context, LOGGER.trace("Evaluating {}", shortDesc); // TODO: it would be nice to cache this // null output definition: this script has no output - ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(scriptExpressionEvaluatorType, null, context.getPrivilegedExpressionProfile(), expressionFactory, shortDesc, task, result); + ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(scriptExpressionEvaluatorType, null, + context.getPrivilegedExpressionProfile(), expressionFactory, shortDesc, result); ExpressionVariables variables = new ExpressionVariables(); variables.put(ExpressionConstants.VAR_PRISM_CONTEXT, prismContext, PrismContext.class); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusConstraintsChecker.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusConstraintsChecker.java index e289467da32..72e799b99f6 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusConstraintsChecker.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusConstraintsChecker.java @@ -7,7 +7,6 @@ package com.evolveum.midpoint.model.impl.lens.projector.focus; import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -231,6 +230,8 @@ public static void clearCacheForDelta(Collection modificati public static class Cache extends AbstractThreadLocalCache { + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(Cache.class.getName() + ".content"); + private final Set conflictFreeNames = ConcurrentHashMap.newKeySet(); static boolean isOk(PolyStringType name, CacheConfigurationManager cacheConfigurationManager) { @@ -296,6 +297,13 @@ protected int getSize() { return conflictFreeNames.size(); } + @Override + protected void dumpContent(String threadName) { + if (LOGGER_CONTENT.isInfoEnabled()) { + conflictFreeNames.forEach(name -> LOGGER_CONTENT.info("Cached conflict-free name [{}]: {}", threadName, name)); + } + } + private static void log(String message, boolean info, Object... params) { CacheUtil.log(LOGGER, PERFORMANCE_ADVISOR, message, info, params); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java index 8b22a3b285e..f24b1fc49a4 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java @@ -43,10 +43,8 @@ import javax.xml.namespace.QName; import java.util.Collection; import java.util.List; -import java.util.Map; import static com.evolveum.midpoint.model.impl.scripting.VariablesUtil.cloneIfNecessary; -import static com.evolveum.midpoint.schema.constants.SchemaConstants.NS_C; /** * @author mederly @@ -87,7 +85,8 @@ public PipelineData execute(ActionExpressionType expression, PipelineData input, ScriptExpression scriptExpression; try { - scriptExpression = scriptExpressionFactory.createScriptExpression(script, outputDefinition, expressionProfile, expressionFactory, "script", context.getTask(), globalResult); + scriptExpression = scriptExpressionFactory.createScriptExpression(script, outputDefinition, expressionProfile, expressionFactory, "script", + globalResult); } catch (ExpressionSyntaxException | SecurityViolationException e) { throw new ScriptExecutionException("Couldn't parse script expression: " + e.getMessage(), e); } diff --git a/model/model-impl/src/main/resources/ctx-model.xml b/model/model-impl/src/main/resources/ctx-model.xml index fe9c2343717..2ae944e9fd9 100644 --- a/model/model-impl/src/main/resources/ctx-model.xml +++ b/model/model-impl/src/main/resources/ctx-model.xml @@ -94,7 +94,6 @@ - diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/expr/TestModelExpressions.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/expr/TestModelExpressions.java index 8d862315721..e85c441a00a 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/expr/TestModelExpressions.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/expr/TestModelExpressions.java @@ -136,7 +136,7 @@ public void testGetManagersOids() throws Exception { PROPERTY_NAME, DOMUtil.XSD_STRING); ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression( scriptType, outputDefinition, MiscSchemaUtil.getExpressionProfile(), - expressionFactory, shortTestName, task, result); + expressionFactory, shortTestName, result); ExpressionVariables variables = createVariables(ExpressionConstants.VAR_USER, chef, chef.getDefinition()); @@ -171,7 +171,7 @@ public void testIsUniquePropertyValue() throws Exception { ScriptExpressionEvaluatorType scriptType = parseScriptType("expression-" + testName + ".xml"); PrismPropertyDefinition outputDefinition = getPrismContext().definitionFactory().createPropertyDefinition(PROPERTY_NAME, DOMUtil.XSD_BOOLEAN); ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(scriptType, outputDefinition, - MiscSchemaUtil.getExpressionProfile(), expressionFactory, testName, task, result); + MiscSchemaUtil.getExpressionProfile(), expressionFactory, testName, result); ExpressionVariables variables = createVariables( ExpressionConstants.VAR_USER, chef, chef.getDefinition(), @@ -292,7 +292,7 @@ private String executeScriptExpressionString(ExpressionVariables variables) PROPERTY_NAME, DOMUtil.XSD_STRING); ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression( scriptType, outputDefinition, MiscSchemaUtil.getExpressionProfile(), - expressionFactory, shortTestName, task, result); + expressionFactory, shortTestName, result); if (variables == null) { variables = new ExpressionVariables(); } diff --git a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportServiceImpl.java b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportServiceImpl.java index 8252ad38d67..f8459141719 100644 --- a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportServiceImpl.java +++ b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportServiceImpl.java @@ -1,440 +1,441 @@ -/* - * Copyright (c) 2010-2019 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.report.impl; - -import java.util.*; -import java.util.Map.Entry; -import javax.xml.namespace.QName; - -import org.apache.commons.lang.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import com.evolveum.midpoint.audit.api.AuditEventRecord; -import com.evolveum.midpoint.audit.api.AuditService; -import com.evolveum.midpoint.model.api.ModelAuthorizationAction; -import com.evolveum.midpoint.model.api.ModelService; -import com.evolveum.midpoint.model.common.ArchetypeManager; -import com.evolveum.midpoint.model.common.expression.ExpressionEnvironment; -import com.evolveum.midpoint.model.common.expression.ModelExpressionThreadLocalHolder; -import com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary; -import com.evolveum.midpoint.model.common.expression.script.ScriptExpression; -import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluationContext; -import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluatorFactory; -import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionFactory; -import com.evolveum.midpoint.model.common.expression.script.groovy.GroovyScriptEvaluator; -import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.query.ObjectFilter; -import com.evolveum.midpoint.prism.query.ObjectQuery; -import com.evolveum.midpoint.prism.query.TypeFilter; -import com.evolveum.midpoint.repo.common.ObjectResolver; -import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; -import com.evolveum.midpoint.repo.common.expression.ExpressionUtil; -import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; -import com.evolveum.midpoint.report.api.ReportService; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.SchemaHelper; -import com.evolveum.midpoint.schema.SelectorOptions; -import com.evolveum.midpoint.schema.expression.*; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.util.ObjectQueryUtil; -import com.evolveum.midpoint.security.enforcer.api.AuthorizationParameters; -import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.task.api.TaskManager; -import com.evolveum.midpoint.util.exception.*; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; - -@Component -public class ReportServiceImpl implements ReportService { - - private static final Trace LOGGER = TraceManager.getTrace(ReportServiceImpl.class); - - @Autowired private ModelService model; - @Autowired private TaskManager taskManager; - @Autowired private PrismContext prismContext; - @Autowired private SchemaHelper schemaHelper; - @Autowired private ExpressionFactory expressionFactory; - @Autowired @Qualifier("modelObjectResolver") private ObjectResolver objectResolver; - @Autowired private AuditService auditService; - @Autowired private FunctionLibrary logFunctionLibrary; - @Autowired private FunctionLibrary basicFunctionLibrary; - @Autowired private FunctionLibrary midpointFunctionLibrary; - @Autowired private SecurityEnforcer securityEnforcer; - @Autowired private ScriptExpressionFactory scriptExpressionFactory; - @Autowired private ArchetypeManager archetypeManager; - - @Override - public ObjectQuery parseQuery(PrismObject report, String query, VariablesMap parameters, Task task, OperationResult result) throws SchemaException, - ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { - if (StringUtils.isBlank(query)) { - return null; - } - - ObjectQuery parsedQuery; - try { - - ExpressionProfile expressionProfile = determineExpressionProfile(report, result); - - ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(task, result)); - SearchFilterType filterType = prismContext.parserFor(query).parseRealValue(SearchFilterType.class); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("filter(SearchFilterType)\n{}", filterType.debugDump(1)); - } - ObjectFilter filter = prismContext.getQueryConverter().parseFilter(filterType, UserType.class); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("filter(ObjectFilter)\n{}", filter.debugDump(1)); - } - if (!(filter instanceof TypeFilter)) { - throw new IllegalArgumentException( - "Defined query must contain type. Use 'type filter' in your report query."); - } - - ObjectFilter subFilter = ((TypeFilter) filter).getFilter(); - ObjectQuery q = prismContext.queryFactory().createQuery(subFilter); - - ExpressionVariables variables = new ExpressionVariables(); - variables.putAll(parameters); - - q = ExpressionUtil.evaluateQueryExpressions(q, variables, expressionProfile, expressionFactory, prismContext, - "parsing expression values for report", task, result); - ((TypeFilter) filter).setFilter(q.getFilter()); - ObjectQueryUtil.simplify(filter, prismContext); - parsedQuery = prismContext.queryFactory().createQuery(filter); - - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("report query (parsed):\n{}", parsedQuery.debugDump(1)); - } - } catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException - | CommunicationException | ConfigurationException | SecurityViolationException e) { - LOGGER.error("Cannot convert query, reason: {}", e.getMessage()); - throw e; - } finally { - ModelExpressionThreadLocalHolder.popExpressionEnvironment(); - } - return parsedQuery; - - } - - @Override - public Collection> searchObjects(ObjectQuery query, Collection> options, Task task, OperationResult parentResult) - throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - // List> results = new ArrayList<>(); - - // GetOperationOptions options = GetOperationOptions.createRaw(); - - if (!(query.getFilter() instanceof TypeFilter)) { - throw new IllegalArgumentException("Query must contain type filter."); - } - - TypeFilter typeFilter = (TypeFilter) query.getFilter(); - QName type = typeFilter.getType(); - Class clazz = prismContext.getSchemaRegistry().determineCompileTimeClass(type); - if (clazz == null) { - PrismObjectDefinition objectDef = prismContext.getSchemaRegistry().findObjectDefinitionByType(type); - if (objectDef == null) { - throw new SchemaException("Undefined object type used in query, type: " + type); - } - clazz = objectDef.getCompileTimeClass(); - } - - ObjectQuery queryForSearch = prismContext.queryFactory().createQuery(typeFilter.getFilter()); - - // options.add(new - // SelectorOptions(GetOperationOptions.createResolveNames())); - GetOperationOptions getOptions = GetOperationOptions.createResolveNames(); - if (ShadowType.class.isAssignableFrom(clazz) && securityEnforcer.isAuthorized(ModelAuthorizationAction.RAW_OPERATION.getUrl(), null, AuthorizationParameters.EMPTY, null, task, parentResult)) { - LOGGER.trace("Setting searching in raw mode."); - getOptions.setRaw(Boolean.TRUE); // shadows in non-raw mode require specifying resource OID and kind (at least) - todo research this further - } else { - LOGGER.trace("Setting searching in noFetch mode. Shadows in non-raw mode require specifying resource OID and objectClass (kind) at least."); - getOptions.setNoFetch(Boolean.TRUE); - } - options = SelectorOptions.createCollection(getOptions); - List> results; - try { - results = model.searchObjects(clazz, queryForSearch, options, task, parentResult); - return results; - } catch (SchemaException | ObjectNotFoundException | SecurityViolationException - | CommunicationException | ConfigurationException | ExpressionEvaluationException e) { - // TODO Auto-generated catch block - throw e; - } - - } - - @Override - public Collection> evaluateScript(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result) - throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { - - ExpressionVariables variables = new ExpressionVariables(); - variables.putAll(parameters); - - TypedValue auditParams = getConvertedParams(parameters); - // special variable for audit report - variables.put("auditParams", auditParams); - - ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext(); - context.setVariables(variables); - context.setContextDescription("report script"); // TODO: improve - context.setTask(task); - context.setResult(result); - setupExpressionProfiles(context, report); - - Object o = evaluateReportScript(script, context, report); - - List> results = new ArrayList<>(); - if (o != null) { - - if (Collection.class.isAssignableFrom(o.getClass())) { - Collection resultSet = (Collection) o; - if (resultSet != null && !resultSet.isEmpty()) { - for (Object obj : resultSet) { - results.add(convertResultingObject(obj)); - } - } - - } else { - results.add(convertResultingObject(o)); - } - } - - return results; - } - - - private Collection runAuditQuery(String sqlWhereClause, TypedValue jasperAuditParams, OperationResult result) { - if (StringUtils.isBlank(sqlWhereClause)) { - return new ArrayList<>(); - } - - String query = "select * from m_audit_event as aer " + sqlWhereClause; - LOGGER.trace("AAAAAAA: query: {}", query); - Map auditParams = ReportUtils.jasperParamsToAuditParams((VariablesMap)jasperAuditParams.getValue()); - LOGGER.trace("AAAAAAA: auditParams:\n{}", auditParams); - List auditRecords = auditService.listRecords(query, auditParams, result); - LOGGER.trace("AAAAAAA: {} records", auditRecords==null?null:auditRecords.size()); - return auditRecords; - } - - @Override - public Object evaluate(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, - ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { - - ExpressionVariables variables = new ExpressionVariables(); - variables.addVariableDefinitions(parameters); - - // special variable for audit report - variables.put("auditParams", getConvertedParams(parameters)); - - ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext(); - context.setVariables(variables); - context.setContextDescription("report script"); // TODO: improve - context.setTask(task); - context.setResult(result); - setupExpressionProfiles(context, report); - - Object o = evaluateReportScript(script, context, report); - - return o; - - } - - protected PrismContainerValue convertResultingObject(Object obj) { - if (obj instanceof PrismObject) { - return ((PrismObject) obj).asObjectable().asPrismContainerValue(); - } else if (obj instanceof Objectable) { - return ((Objectable) obj).asPrismContainerValue(); - } else if (obj instanceof PrismContainerValue) { - return (PrismContainerValue) obj; - } else if (obj instanceof Containerable) { - return ((Containerable) obj).asPrismContainerValue(); - } else { - throw new IllegalStateException("Reporting script should return something compatible with PrismContainerValue, not a " + obj.getClass()); - } - } - - @Override - public Collection evaluateAuditScript(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result) - throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { - Collection results = new ArrayList<>(); - - TypedValue auditParams = getConvertedParams(parameters); - ExpressionVariables variables = new ExpressionVariables(); - variables.put("auditParams", auditParams); - - ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext(); - context.setVariables(variables); - - context.setContextDescription("report script"); // TODO: improve - context.setTask(task); - context.setResult(result); - setupExpressionProfiles(context, report); - - Object o = evaluateReportScript(script, context, report); - - // HACK to allow audit reports where query is just a plain string. - // Oh my, this code is a mess. This needs a real rewrite. MID-5572 - if (o instanceof String) { - JasperReportEngineConfigurationType jasperConfig = report.asObjectable().getJasper(); - if (jasperConfig == null) { - throw new SchemaException("Jasper reportType not set, cannot determine how to use string query"); - } - JasperReportTypeType reportType = jasperConfig.getReportType(); - if (reportType == null) { - throw new SchemaException("Jasper reportType not set, cannot determine how to use string query"); - } - if (reportType.equals(JasperReportTypeType.AUDIT_SQL)) { - return runAuditQuery((String)o, auditParams, result); - } else { - throw new SchemaException("Jasper reportType is not set to auditSql, cannot determine how to use string query"); - } - } - - if (o != null) { - - if (Collection.class.isAssignableFrom(o.getClass())) { - Collection resultSet = (Collection) o; - if (resultSet != null && !resultSet.isEmpty()) { - for (Object obj : resultSet) { - if (!(obj instanceof AuditEventRecord)) { - LOGGER.warn("Skipping result, not an audit event record " + obj); - continue; - } - results.add((AuditEventRecord) obj); - } - - } - - } else { - results.add((AuditEventRecord) o); - } - } - - return results; - } - - private TypedValue getConvertedParams(VariablesMap parameters) { - if (parameters == null) { - return new TypedValue<>(null, VariablesMap.class); - } - - VariablesMap resultParamMap = new VariablesMap(); - Set> paramEntries = parameters.entrySet(); - for (Entry e : paramEntries) { - Object value = e.getValue().getValue(); - if (value instanceof PrismPropertyValue) { - resultParamMap.put(e.getKey(), e.getValue().createTransformed(((PrismPropertyValue) value).getValue())); - } else { - resultParamMap.put(e.getKey(), e.getValue()); - } - } - - return new TypedValue<>(resultParamMap, VariablesMap.class); - } - - private Collection createFunctionLibraries() { - FunctionLibrary midPointLib = new FunctionLibrary(); - midPointLib.setVariableName("report"); - midPointLib.setNamespace("http://midpoint.evolveum.com/xml/ns/public/function/report-3"); - ReportFunctions reportFunctions = new ReportFunctions(prismContext, schemaHelper, model, taskManager, auditService); - midPointLib.setGenericFunctions(reportFunctions); - - Collection functions = new ArrayList<>(); - functions.add(basicFunctionLibrary); - functions.add(logFunctionLibrary); - functions.add(midpointFunctionLibrary); - functions.add(midPointLib); - return functions; - } - - @Override - public PrismContext getPrismContext() { - return prismContext; - } - - public Object evaluateReportScript(String codeString, ScriptExpressionEvaluationContext context, PrismObject report) throws ExpressionEvaluationException, - ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, SchemaException { - - ScriptExpressionEvaluatorConfigurationType defaultScriptConfiguration = report.asObjectable().getDefaultScriptConfiguration(); - ScriptExpressionEvaluatorType expressionType = new ScriptExpressionEvaluatorType(); - expressionType.setCode(codeString); - expressionType.setObjectVariableMode(defaultScriptConfiguration == null ? ObjectVariableModeType.OBJECT : defaultScriptConfiguration.getObjectVariableMode()); - context.setExpressionType(expressionType); - // Be careful about output definition here. We really do NOT want to set it. - // Not setting the definition means that we want raw value without any conversion. - // This is what we really want, because there may be exotic things such as JRTemplate going through those expressions - // We do not have any reasonable prism definitions for those. - - context.setFunctions(createFunctionLibraries()); - context.setObjectResolver(objectResolver); - - ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression( - expressionType, context.getOutputDefinition(), context.getExpressionProfile(), expressionFactory, context.getContextDescription(), context.getTask(), context.getResult()); - - ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(context.getTask(), context.getResult())); - List expressionResult; - try { - expressionResult = scriptExpression.evaluate(context); - } finally { - ModelExpressionThreadLocalHolder.popExpressionEnvironment(); - } - - if (expressionResult == null || expressionResult.isEmpty()) { - return null; - } - if (expressionResult.size() > 1) { - throw new ExpressionEvaluationException("Too many results from expression "+context.getContextDescription()); - } - return expressionResult.get(0).getRealValue(); - } - - private ExpressionProfile determineExpressionProfile(PrismObject report, OperationResult result) throws SchemaException, ConfigurationException { - if (report == null) { - throw new IllegalArgumentException("No report defined, cannot determine profile"); - } - return archetypeManager.determineExpressionProfile(report, result); - } - - private void setupExpressionProfiles(ScriptExpressionEvaluationContext context, PrismObject report) throws SchemaException, ConfigurationException { - ExpressionProfile expressionProfile = determineExpressionProfile(report, context.getResult()); - LOGGER.trace("Using expression profile '"+(expressionProfile==null?null:expressionProfile.getIdentifier())+"' for report evaluation, determined from: {}", report); - context.setExpressionProfile(expressionProfile); - context.setScriptExpressionProfile(findScriptExpressionProfile(expressionProfile, report)); - } - - private ScriptExpressionProfile findScriptExpressionProfile(ExpressionProfile expressionProfile, PrismObject report) { - if (expressionProfile == null) { - return null; - } - ExpressionEvaluatorProfile scriptEvaluatorProfile = expressionProfile.getEvaluatorProfile(ScriptExpressionEvaluatorFactory.ELEMENT_NAME); - if (scriptEvaluatorProfile == null) { - return null; - } - return scriptEvaluatorProfile.getScriptExpressionProfile(getScriptLanguageName(report)); - } - - private String getScriptLanguageName(PrismObject report) { - // Hardcoded for now - return GroovyScriptEvaluator.LANGUAGE_NAME; - } - - @Override - public PrismObject getReportDefinition(String reportOid, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - return model.getObject(ReportType.class, reportOid, null, task, result); - } - - @Override - public boolean isAuthorizedToRunReport(PrismObject report, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { - AuthorizationParameters params = AuthorizationParameters.Builder.buildObject(report); - return securityEnforcer.isAuthorized(ModelAuthorizationAction.RUN_REPORT.getUrl(), null, params, null, task, result); - } -} +/* + * Copyright (c) 2010-2019 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.report.impl; + +import java.util.*; +import java.util.Map.Entry; +import javax.xml.namespace.QName; + +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import com.evolveum.midpoint.audit.api.AuditEventRecord; +import com.evolveum.midpoint.audit.api.AuditService; +import com.evolveum.midpoint.model.api.ModelAuthorizationAction; +import com.evolveum.midpoint.model.api.ModelService; +import com.evolveum.midpoint.model.common.ArchetypeManager; +import com.evolveum.midpoint.model.common.expression.ExpressionEnvironment; +import com.evolveum.midpoint.model.common.expression.ModelExpressionThreadLocalHolder; +import com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary; +import com.evolveum.midpoint.model.common.expression.script.ScriptExpression; +import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluationContext; +import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluatorFactory; +import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionFactory; +import com.evolveum.midpoint.model.common.expression.script.groovy.GroovyScriptEvaluator; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.query.ObjectFilter; +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.prism.query.TypeFilter; +import com.evolveum.midpoint.repo.common.ObjectResolver; +import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; +import com.evolveum.midpoint.repo.common.expression.ExpressionUtil; +import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; +import com.evolveum.midpoint.report.api.ReportService; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.SchemaHelper; +import com.evolveum.midpoint.schema.SelectorOptions; +import com.evolveum.midpoint.schema.expression.*; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ObjectQueryUtil; +import com.evolveum.midpoint.security.enforcer.api.AuthorizationParameters; +import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.task.api.TaskManager; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; + +@Component +public class ReportServiceImpl implements ReportService { + + private static final Trace LOGGER = TraceManager.getTrace(ReportServiceImpl.class); + + @Autowired private ModelService model; + @Autowired private TaskManager taskManager; + @Autowired private PrismContext prismContext; + @Autowired private SchemaHelper schemaHelper; + @Autowired private ExpressionFactory expressionFactory; + @Autowired @Qualifier("modelObjectResolver") private ObjectResolver objectResolver; + @Autowired private AuditService auditService; + @Autowired private FunctionLibrary logFunctionLibrary; + @Autowired private FunctionLibrary basicFunctionLibrary; + @Autowired private FunctionLibrary midpointFunctionLibrary; + @Autowired private SecurityEnforcer securityEnforcer; + @Autowired private ScriptExpressionFactory scriptExpressionFactory; + @Autowired private ArchetypeManager archetypeManager; + + @Override + public ObjectQuery parseQuery(PrismObject report, String query, VariablesMap parameters, Task task, OperationResult result) throws SchemaException, + ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { + if (StringUtils.isBlank(query)) { + return null; + } + + ObjectQuery parsedQuery; + try { + + ExpressionProfile expressionProfile = determineExpressionProfile(report, result); + + ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(task, result)); + SearchFilterType filterType = prismContext.parserFor(query).parseRealValue(SearchFilterType.class); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("filter(SearchFilterType)\n{}", filterType.debugDump(1)); + } + ObjectFilter filter = prismContext.getQueryConverter().parseFilter(filterType, UserType.class); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("filter(ObjectFilter)\n{}", filter.debugDump(1)); + } + if (!(filter instanceof TypeFilter)) { + throw new IllegalArgumentException( + "Defined query must contain type. Use 'type filter' in your report query."); + } + + ObjectFilter subFilter = ((TypeFilter) filter).getFilter(); + ObjectQuery q = prismContext.queryFactory().createQuery(subFilter); + + ExpressionVariables variables = new ExpressionVariables(); + variables.putAll(parameters); + + q = ExpressionUtil.evaluateQueryExpressions(q, variables, expressionProfile, expressionFactory, prismContext, + "parsing expression values for report", task, result); + ((TypeFilter) filter).setFilter(q.getFilter()); + ObjectQueryUtil.simplify(filter, prismContext); + parsedQuery = prismContext.queryFactory().createQuery(filter); + + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("report query (parsed):\n{}", parsedQuery.debugDump(1)); + } + } catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException + | CommunicationException | ConfigurationException | SecurityViolationException e) { + LOGGER.error("Cannot convert query, reason: {}", e.getMessage()); + throw e; + } finally { + ModelExpressionThreadLocalHolder.popExpressionEnvironment(); + } + return parsedQuery; + + } + + @Override + public Collection> searchObjects(ObjectQuery query, Collection> options, Task task, OperationResult parentResult) + throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + // List> results = new ArrayList<>(); + + // GetOperationOptions options = GetOperationOptions.createRaw(); + + if (!(query.getFilter() instanceof TypeFilter)) { + throw new IllegalArgumentException("Query must contain type filter."); + } + + TypeFilter typeFilter = (TypeFilter) query.getFilter(); + QName type = typeFilter.getType(); + Class clazz = prismContext.getSchemaRegistry().determineCompileTimeClass(type); + if (clazz == null) { + PrismObjectDefinition objectDef = prismContext.getSchemaRegistry().findObjectDefinitionByType(type); + if (objectDef == null) { + throw new SchemaException("Undefined object type used in query, type: " + type); + } + clazz = objectDef.getCompileTimeClass(); + } + + ObjectQuery queryForSearch = prismContext.queryFactory().createQuery(typeFilter.getFilter()); + + // options.add(new + // SelectorOptions(GetOperationOptions.createResolveNames())); + GetOperationOptions getOptions = GetOperationOptions.createResolveNames(); + if (ShadowType.class.isAssignableFrom(clazz) && securityEnforcer.isAuthorized(ModelAuthorizationAction.RAW_OPERATION.getUrl(), null, AuthorizationParameters.EMPTY, null, task, parentResult)) { + LOGGER.trace("Setting searching in raw mode."); + getOptions.setRaw(Boolean.TRUE); // shadows in non-raw mode require specifying resource OID and kind (at least) - todo research this further + } else { + LOGGER.trace("Setting searching in noFetch mode. Shadows in non-raw mode require specifying resource OID and objectClass (kind) at least."); + getOptions.setNoFetch(Boolean.TRUE); + } + options = SelectorOptions.createCollection(getOptions); + List> results; + try { + results = model.searchObjects(clazz, queryForSearch, options, task, parentResult); + return results; + } catch (SchemaException | ObjectNotFoundException | SecurityViolationException + | CommunicationException | ConfigurationException | ExpressionEvaluationException e) { + // TODO Auto-generated catch block + throw e; + } + + } + + @Override + public Collection> evaluateScript(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result) + throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { + + ExpressionVariables variables = new ExpressionVariables(); + variables.putAll(parameters); + + TypedValue auditParams = getConvertedParams(parameters); + // special variable for audit report + variables.put("auditParams", auditParams); + + ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext(); + context.setVariables(variables); + context.setContextDescription("report script"); // TODO: improve + context.setTask(task); + context.setResult(result); + setupExpressionProfiles(context, report); + + Object o = evaluateReportScript(script, context, report); + + List> results = new ArrayList<>(); + if (o != null) { + + if (Collection.class.isAssignableFrom(o.getClass())) { + Collection resultSet = (Collection) o; + if (resultSet != null && !resultSet.isEmpty()) { + for (Object obj : resultSet) { + results.add(convertResultingObject(obj)); + } + } + + } else { + results.add(convertResultingObject(o)); + } + } + + return results; + } + + + private Collection runAuditQuery(String sqlWhereClause, TypedValue jasperAuditParams, OperationResult result) { + if (StringUtils.isBlank(sqlWhereClause)) { + return new ArrayList<>(); + } + + String query = "select * from m_audit_event as aer " + sqlWhereClause; + LOGGER.trace("AAAAAAA: query: {}", query); + Map auditParams = ReportUtils.jasperParamsToAuditParams((VariablesMap)jasperAuditParams.getValue()); + LOGGER.trace("AAAAAAA: auditParams:\n{}", auditParams); + List auditRecords = auditService.listRecords(query, auditParams, result); + LOGGER.trace("AAAAAAA: {} records", auditRecords==null?null:auditRecords.size()); + return auditRecords; + } + + @Override + public Object evaluate(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, + ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { + + ExpressionVariables variables = new ExpressionVariables(); + variables.addVariableDefinitions(parameters); + + // special variable for audit report + variables.put("auditParams", getConvertedParams(parameters)); + + ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext(); + context.setVariables(variables); + context.setContextDescription("report script"); // TODO: improve + context.setTask(task); + context.setResult(result); + setupExpressionProfiles(context, report); + + Object o = evaluateReportScript(script, context, report); + + return o; + + } + + protected PrismContainerValue convertResultingObject(Object obj) { + if (obj instanceof PrismObject) { + return ((PrismObject) obj).asObjectable().asPrismContainerValue(); + } else if (obj instanceof Objectable) { + return ((Objectable) obj).asPrismContainerValue(); + } else if (obj instanceof PrismContainerValue) { + return (PrismContainerValue) obj; + } else if (obj instanceof Containerable) { + return ((Containerable) obj).asPrismContainerValue(); + } else { + throw new IllegalStateException("Reporting script should return something compatible with PrismContainerValue, not a " + obj.getClass()); + } + } + + @Override + public Collection evaluateAuditScript(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result) + throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException { + Collection results = new ArrayList<>(); + + TypedValue auditParams = getConvertedParams(parameters); + ExpressionVariables variables = new ExpressionVariables(); + variables.put("auditParams", auditParams); + + ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext(); + context.setVariables(variables); + + context.setContextDescription("report script"); // TODO: improve + context.setTask(task); + context.setResult(result); + setupExpressionProfiles(context, report); + + Object o = evaluateReportScript(script, context, report); + + // HACK to allow audit reports where query is just a plain string. + // Oh my, this code is a mess. This needs a real rewrite. MID-5572 + if (o instanceof String) { + JasperReportEngineConfigurationType jasperConfig = report.asObjectable().getJasper(); + if (jasperConfig == null) { + throw new SchemaException("Jasper reportType not set, cannot determine how to use string query"); + } + JasperReportTypeType reportType = jasperConfig.getReportType(); + if (reportType == null) { + throw new SchemaException("Jasper reportType not set, cannot determine how to use string query"); + } + if (reportType.equals(JasperReportTypeType.AUDIT_SQL)) { + return runAuditQuery((String)o, auditParams, result); + } else { + throw new SchemaException("Jasper reportType is not set to auditSql, cannot determine how to use string query"); + } + } + + if (o != null) { + + if (Collection.class.isAssignableFrom(o.getClass())) { + Collection resultSet = (Collection) o; + if (resultSet != null && !resultSet.isEmpty()) { + for (Object obj : resultSet) { + if (!(obj instanceof AuditEventRecord)) { + LOGGER.warn("Skipping result, not an audit event record " + obj); + continue; + } + results.add((AuditEventRecord) obj); + } + + } + + } else { + results.add((AuditEventRecord) o); + } + } + + return results; + } + + private TypedValue getConvertedParams(VariablesMap parameters) { + if (parameters == null) { + return new TypedValue<>(null, VariablesMap.class); + } + + VariablesMap resultParamMap = new VariablesMap(); + Set> paramEntries = parameters.entrySet(); + for (Entry e : paramEntries) { + Object value = e.getValue().getValue(); + if (value instanceof PrismPropertyValue) { + resultParamMap.put(e.getKey(), e.getValue().createTransformed(((PrismPropertyValue) value).getValue())); + } else { + resultParamMap.put(e.getKey(), e.getValue()); + } + } + + return new TypedValue<>(resultParamMap, VariablesMap.class); + } + + private Collection createFunctionLibraries() { + FunctionLibrary midPointLib = new FunctionLibrary(); + midPointLib.setVariableName("report"); + midPointLib.setNamespace("http://midpoint.evolveum.com/xml/ns/public/function/report-3"); + ReportFunctions reportFunctions = new ReportFunctions(prismContext, schemaHelper, model, taskManager, auditService); + midPointLib.setGenericFunctions(reportFunctions); + + Collection functions = new ArrayList<>(); + functions.add(basicFunctionLibrary); + functions.add(logFunctionLibrary); + functions.add(midpointFunctionLibrary); + functions.add(midPointLib); + return functions; + } + + @Override + public PrismContext getPrismContext() { + return prismContext; + } + + public Object evaluateReportScript(String codeString, ScriptExpressionEvaluationContext context, PrismObject report) throws ExpressionEvaluationException, + ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, SchemaException { + + ScriptExpressionEvaluatorConfigurationType defaultScriptConfiguration = report.asObjectable().getDefaultScriptConfiguration(); + ScriptExpressionEvaluatorType expressionType = new ScriptExpressionEvaluatorType(); + expressionType.setCode(codeString); + expressionType.setObjectVariableMode(defaultScriptConfiguration == null ? ObjectVariableModeType.OBJECT : defaultScriptConfiguration.getObjectVariableMode()); + context.setExpressionType(expressionType); + // Be careful about output definition here. We really do NOT want to set it. + // Not setting the definition means that we want raw value without any conversion. + // This is what we really want, because there may be exotic things such as JRTemplate going through those expressions + // We do not have any reasonable prism definitions for those. + + context.setFunctions(createFunctionLibraries()); + context.setObjectResolver(objectResolver); + + ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression( + expressionType, context.getOutputDefinition(), context.getExpressionProfile(), expressionFactory, context.getContextDescription(), + context.getResult()); + + ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(context.getTask(), context.getResult())); + List expressionResult; + try { + expressionResult = scriptExpression.evaluate(context); + } finally { + ModelExpressionThreadLocalHolder.popExpressionEnvironment(); + } + + if (expressionResult == null || expressionResult.isEmpty()) { + return null; + } + if (expressionResult.size() > 1) { + throw new ExpressionEvaluationException("Too many results from expression "+context.getContextDescription()); + } + return expressionResult.get(0).getRealValue(); + } + + private ExpressionProfile determineExpressionProfile(PrismObject report, OperationResult result) throws SchemaException, ConfigurationException { + if (report == null) { + throw new IllegalArgumentException("No report defined, cannot determine profile"); + } + return archetypeManager.determineExpressionProfile(report, result); + } + + private void setupExpressionProfiles(ScriptExpressionEvaluationContext context, PrismObject report) throws SchemaException, ConfigurationException { + ExpressionProfile expressionProfile = determineExpressionProfile(report, context.getResult()); + LOGGER.trace("Using expression profile '"+(expressionProfile==null?null:expressionProfile.getIdentifier())+"' for report evaluation, determined from: {}", report); + context.setExpressionProfile(expressionProfile); + context.setScriptExpressionProfile(findScriptExpressionProfile(expressionProfile, report)); + } + + private ScriptExpressionProfile findScriptExpressionProfile(ExpressionProfile expressionProfile, PrismObject report) { + if (expressionProfile == null) { + return null; + } + ExpressionEvaluatorProfile scriptEvaluatorProfile = expressionProfile.getEvaluatorProfile(ScriptExpressionEvaluatorFactory.ELEMENT_NAME); + if (scriptEvaluatorProfile == null) { + return null; + } + return scriptEvaluatorProfile.getScriptExpressionProfile(getScriptLanguageName(report)); + } + + private String getScriptLanguageName(PrismObject report) { + // Hardcoded for now + return GroovyScriptEvaluator.LANGUAGE_NAME; + } + + @Override + public PrismObject getReportDefinition(String reportOid, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + return model.getObject(ReportType.class, reportOid, null, task, result); + } + + @Override + public boolean isAuthorizedToRunReport(PrismObject report, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { + AuthorizationParameters params = AuthorizationParameters.Builder.buildObject(report); + return securityEnforcer.isAuthorized(ModelAuthorizationAction.RUN_REPORT.getUrl(), null, params, null, task, result); + } +} diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConfiguredConnectorInstanceEntry.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConfiguredConnectorInstanceEntry.java index ab7b3d429b0..fe27d7aa9e7 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConfiguredConnectorInstanceEntry.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConfiguredConnectorInstanceEntry.java @@ -48,4 +48,11 @@ public void setConnectorInstance(ConnectorInstance connectorInstance) { this.connectorInstance = connectorInstance; } + @Override + public String toString() { + return "ConfiguredConnectorInstanceEntry{" + + "connectorOid='" + connectorOid + '\'' + + ", connectorInstance=" + connectorInstance + + '}'; + } } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConnectorManager.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConnectorManager.java index 36ab2500462..938641ce982 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConnectorManager.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConnectorManager.java @@ -97,6 +97,7 @@ public void unregister() { } private static final Trace LOGGER = TraceManager.getTrace(ConnectorManager.class); + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(ConnectorManager.class.getName() + ".content"); private static final String CONNECTOR_INSTANCE_CACHE_NAME = ConnectorManager.class.getName() + ".connectorInstanceCache"; private static final String CONNECTOR_TYPE_CACHE_NAME = ConnectorManager.class.getName() + ".connectorTypeCache"; @@ -691,4 +692,12 @@ public Collection getStateInformation() { .size(connectorBeanCache.size()) ); } + + @Override + public void dumpContent() { + if (LOGGER_CONTENT.isInfoEnabled()) { + connectorInstanceCache.forEach((k, v) -> LOGGER_CONTENT.info("Cached connector instance: {}: {}", k, v)); + connectorBeanCache.forEach((k, v) -> LOGGER_CONTENT.info("Cached connector bean: {}: {}", k, v)); + } + } } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConstraintsChecker.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConstraintsChecker.java index 388e784e247..ff4abd54817 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConstraintsChecker.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConstraintsChecker.java @@ -43,7 +43,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstraintsCheckingStrategyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; -import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; + import org.jetbrains.annotations.NotNull; /** @@ -383,6 +383,8 @@ public String toString() { public static class Cache extends AbstractThreadLocalCache { + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(Cache.class.getName() + ".content"); + private final Set conflictFreeSituations = ConcurrentHashMap.newKeySet(); private static boolean isOk(String resourceOid, String knownShadowOid, QName objectClassName, QName attributeName, @@ -455,14 +457,6 @@ private static Cache getCache() { return cacheInstances.get(Thread.currentThread()); } - public static void remove(PolyStringType name) { - Cache cache = getCache(); - if (name != null && cache != null) { - log("Cache REMOVE for {}", false, name); - cache.conflictFreeSituations.remove(name.getOrig()); - } - } - @Override public String description() { return "conflict-free situations: " + conflictFreeSituations; @@ -472,6 +466,14 @@ public String description() { protected int getSize() { return conflictFreeSituations.size(); } + + @Override + protected void dumpContent(String threadName) { + if (LOGGER_CONTENT.isInfoEnabled()) { + conflictFreeSituations.forEach(situation -> + LOGGER_CONTENT.info("Cached conflict-free situation [{}]: {}", threadName, situation)); + } + } } private static void log(String message, boolean info, Object... params) { diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java index 3b9cb3ec1da..956b4d7c7f8 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java @@ -48,6 +48,7 @@ public class ResourceCache implements Cacheable { private static final Trace LOGGER = TraceManager.getTrace(ResourceCache.class); + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(ResourceCache.class.getName() + ".content"); @Autowired private PrismContext prismContext; @Autowired private CacheRegistry cacheRegistry; @@ -65,8 +66,8 @@ public void unregister() { } /** - * Note that prism objects in this map are always immutable. And they must remain immutable after getting them - * from the cache. + * Note that prism objects in this map are always not null and immutable. + * And they must remain immutable after getting them from the cache. * * As for ConcurrentHashMap: Although we use synchronization whenever possible, let's be extra cautious here. */ @@ -199,4 +200,12 @@ public synchronized Collection getStateInformat .size(cache.size()) ); } + + @Override + public void dumpContent() { + if (LOGGER_CONTENT.isInfoEnabled()) { + cache.forEach((oid, resource) -> LOGGER_CONTENT.info("Cached resource: {}: {} (version: {})", + oid, resource, resource.getVersion())); + } + } } diff --git a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/Cacheable.java b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/Cacheable.java index 5ba577e05ab..79c2edddc39 100644 --- a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/Cacheable.java +++ b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/Cacheable.java @@ -18,4 +18,6 @@ public interface Cacheable { @NotNull Collection getStateInformation(); + + void dumpContent(); } diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/AbstractGlobalCacheValue.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/AbstractGlobalCacheValue.java new file mode 100644 index 00000000000..8107d4b2652 --- /dev/null +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/AbstractGlobalCacheValue.java @@ -0,0 +1,21 @@ +/* + * 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.repo.cache; + +class AbstractGlobalCacheValue { + + /** + * When the value was crated. Used only for diagnostic purposes. + * (Cache eviction is managed by cache2k itself!) + */ + private final long createdAt = System.currentTimeMillis(); + + long getAge() { + return System.currentTimeMillis() - createdAt; + } +} diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheDispatcherImpl.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheDispatcherImpl.java index 3b161aef63a..cb659c14ef6 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheDispatcherImpl.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheDispatcherImpl.java @@ -12,7 +12,6 @@ import com.evolveum.midpoint.repo.api.CacheListener; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.UserSessionManagementType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import java.util.ArrayList; @@ -26,7 +25,7 @@ public class CacheDispatcherImpl implements CacheDispatcher { private static final Trace LOGGER = TraceManager.getTrace(CacheDispatcherImpl.class); - private List cacheListeners = new ArrayList<>(); + private final List cacheListeners = new ArrayList<>(); @Override public synchronized void registerCacheListener(CacheListener cacheListener) { @@ -53,5 +52,4 @@ public void dispatchInvalidation(Class type, String oi listener.invalidate(type, oid, clusterwide, context); } } - } diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheRegistry.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheRegistry.java index 727346f8b90..591f779ba30 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheRegistry.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheRegistry.java @@ -25,7 +25,7 @@ @Component public class CacheRegistry implements CacheListener { - private List cacheableServices = new ArrayList<>(); + private final List cacheableServices = new ArrayList<>(); @Autowired private CacheDispatcher dispatcher; @Autowired private PrismContext prismContext; @@ -69,5 +69,9 @@ public CachesStateInformationType getStateInformation() { cacheableServices.forEach(cacheable -> rv.getEntry().addAll(cacheable.getStateInformation())); return rv; } + + public void dumpContent() { + cacheableServices.forEach(Cacheable::dumpContent); + } } diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectValue.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectValue.java index 9a0a6744f25..d8d40023ebf 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectValue.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectValue.java @@ -10,48 +10,49 @@ import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import org.jetbrains.annotations.NotNull; + /** * Created by Viliam Repan (lazyman). */ -public class GlobalCacheObjectValue { +class GlobalCacheObjectValue extends AbstractGlobalCacheValue { - private PrismObject object; + @NotNull private final PrismObject object; - private long timeToLive; + private long timeToCheckVersion; - public GlobalCacheObjectValue(PrismObject object, long timeToLive) { + GlobalCacheObjectValue(@NotNull PrismObject object, long timeToCheckVersion) { this.object = object; - this.timeToLive = timeToLive; + this.timeToCheckVersion = timeToCheckVersion; } - public long getTimeToLive() { - return timeToLive; + long getTimeToCheckVersion() { + return timeToCheckVersion; } - public String getObjectOid() { + String getObjectOid() { return object.getOid(); } - public Class getObjectType() { + Class getObjectType() { return object.getCompileTimeClass(); } - public String getObjectVersion() { + String getObjectVersion() { return object.getVersion(); } - public PrismObject getObject() { + @NotNull PrismObject getObject() { return object; // cloning is done in RepositoryCache } - public void setTimeToLive(long timeToLive) { - this.timeToLive = timeToLive; + void setTimeToCheckVersion(long timeToCheckVersion) { + this.timeToCheckVersion = timeToCheckVersion; } @Override public String toString() { - return "CacheObject{" + "ttl=" + timeToLive - + ", object=" + object - + '}'; + return "GlobalCacheObjectValue{" + "timeToCheckVersion=" + timeToCheckVersion + " (" + (timeToCheckVersion-System.currentTimeMillis()) + " left)" + + ", object=" + object + " (version " + object.getVersion() + ")}"; } } diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectVersionValue.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectVersionValue.java index 70ceb0f02e8..11dc9491122 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectVersionValue.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectVersionValue.java @@ -10,10 +10,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import org.jetbrains.annotations.NotNull; -/** - * - */ -public class GlobalCacheObjectVersionValue { +public class GlobalCacheObjectVersionValue extends AbstractGlobalCacheValue { @NotNull private final Class objectType; private final String version; diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheQueryValue.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheQueryValue.java new file mode 100644 index 00000000000..051d2a71bca --- /dev/null +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheQueryValue.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010-2019 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.cache; + +import com.evolveum.midpoint.schema.SearchResultList; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; + +public class GlobalCacheQueryValue extends AbstractGlobalCacheValue { + + @NotNull private final SearchResultList result; + + GlobalCacheQueryValue(@NotNull SearchResultList result) { + this.result = result; + } + + public @NotNull SearchResultList getResult() { + return result; + } + + @Override + public String toString() { + return "GlobalCacheQueryValue{" + + "result=" + result + + '}'; + } +} diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalObjectCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalObjectCache.java index ac738f397e3..c785f245596 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalObjectCache.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalObjectCache.java @@ -31,6 +31,7 @@ public class GlobalObjectCache extends AbstractGlobalCache { private static final Trace LOGGER = TraceManager.getTrace(GlobalObjectCache.class); + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(GlobalObjectCache.class.getName() + ".content"); private static final String CACHE_NAME = "objectCache"; @@ -140,4 +141,15 @@ Collection getStateInformation() { return Collections.emptySet(); } } + + void dumpContent() { + if (cache != null && LOGGER_CONTENT.isInfoEnabled()) { + cache.invokeAll(cache.keys(), e -> { + String key = e.getKey(); + GlobalCacheObjectValue value = e.getValue(); + LOGGER_CONTENT.info("Cached object: {}: {} (cached {} ms ago)", key, value, value.getAge()); + return null; + }); + } + } } diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalQueryCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalQueryCache.java index fa20f1281fa..3136bfa1784 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalQueryCache.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalQueryCache.java @@ -14,9 +14,12 @@ import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SingleCacheStateInformationType; + +import org.apache.commons.lang3.tuple.MutablePair; import org.cache2k.Cache2kBuilder; import org.cache2k.expiry.ExpiryPolicy; import org.cache2k.processor.EntryProcessor; +import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Component; import javax.annotation.PreDestroy; @@ -33,10 +36,11 @@ public class GlobalQueryCache extends AbstractGlobalCache { private static final Trace LOGGER = TraceManager.getTrace(GlobalQueryCache.class); + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(GlobalQueryCache.class.getName() + ".content"); private static final String CACHE_NAME = "queryCache"; - private org.cache2k.Cache cache; + private org.cache2k.Cache cache; public void initialize() { if (cache != null) { @@ -48,7 +52,7 @@ public void initialize() { LOGGER.warn("Capacity for " + getCacheType() + " is set to 0; this cache will be disabled (until system restart)"); cache = null; } else { - cache = new Cache2kBuilder() {} + cache = new Cache2kBuilder() {} .name(CACHE_NAME) .entryCapacity(capacity) .expiryPolicy(getExpirePolicy()) @@ -57,7 +61,7 @@ public void initialize() { } } - private ExpiryPolicy getExpirePolicy() { + private ExpiryPolicy getExpirePolicy() { return (key, value, loadTime, oldEntry) -> getExpiryTime(key.getType()); } @@ -74,8 +78,13 @@ public boolean isAvailable() { } public SearchResultList> get(QueryKey key) { - //noinspection unchecked - return cache != null ? cache.peek(key) : null; + if (cache != null) { + GlobalCacheQueryValue value = cache.peek(key); + //noinspection unchecked + return value != null ? value.getResult() : null; + } else { + return null; + } } public void remove(QueryKey cacheKey) { @@ -84,13 +93,14 @@ public void remove(QueryKey cacheKey) { } } - public void put(QueryKey key, SearchResultList> cacheObject) { + public void put(QueryKey key, @NotNull SearchResultList> cacheObject) { if (cache != null) { - cache.put(key, cacheObject); + //noinspection unchecked + cache.put(key, new GlobalCacheQueryValue(cacheObject)); } } - public void invokeAll(EntryProcessor entryProcessor) { + void invokeAll(EntryProcessor entryProcessor) { if (cache != null) { cache.invokeAll(cache.keys(), entryProcessor); } @@ -113,25 +123,50 @@ public void clear() { } Collection getStateInformation() { - Map, Integer> counts = new HashMap<>(); - AtomicInteger size = new AtomicInteger(0); + Map, MutablePair> counts = new HashMap<>(); + AtomicInteger queries = new AtomicInteger(0); + AtomicInteger objects = new AtomicInteger(0); if (cache != null) { cache.invokeAll(cache.keys(), e -> { - Class objectType = e.getKey().getType(); - counts.compute(objectType, (type, count) -> count != null ? count+1 : 1); - size.incrementAndGet(); + QueryKey queryKey = e.getKey(); + Class objectType = queryKey.getType(); + int resultingObjects = e.getValue().getResult().size(); + MutablePair value = counts.get(objectType); + if (value == null) { + value = new MutablePair<>(0, 0); + counts.put(objectType, value); + } + value.setLeft(value.getLeft() + 1); + value.setRight(value.getRight() + resultingObjects); + queries.incrementAndGet(); + objects.addAndGet(resultingObjects); return null; }); SingleCacheStateInformationType info = new SingleCacheStateInformationType(prismContext) .name(GlobalQueryCache.class.getName()) - .size(size.get()); - counts.forEach((type, count) -> + .size(queries.get()) + .secondarySize(objects.get()); + counts.forEach((type, pair) -> info.beginComponent() .name(type.getSimpleName()) - .size(count)); + .size(pair.getLeft()) + .secondarySize(pair.getRight())); return Collections.singleton(info); } else { return Collections.emptySet(); } } + + void dumpContent() { + if (cache != null && LOGGER_CONTENT.isInfoEnabled()) { + cache.invokeAll(cache.keys(), e -> { + QueryKey key = e.getKey(); + GlobalCacheQueryValue value = e.getValue(); + @NotNull SearchResultList queryResult = value.getResult(); + LOGGER_CONTENT.info("Cached query of {} ({} object(s), cached {} ms ago): {}: {}", + key.getType().getSimpleName(), queryResult.size(), value.getAge(), key.getQuery(), queryResult); + return null; + }); + } + } } diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalVersionCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalVersionCache.java index a44ab4a027a..7ee451c4b61 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalVersionCache.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalVersionCache.java @@ -32,6 +32,7 @@ public class GlobalVersionCache extends AbstractGlobalCache { private static final Trace LOGGER = TraceManager.getTrace(GlobalVersionCache.class); + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(GlobalVersionCache.class.getName() + ".content"); private static final String CACHE_NAME = "versionCache"; @@ -149,4 +150,13 @@ public void put(String oid, Class type, String version) { cache.put(oid, new GlobalCacheObjectVersionValue<>(type, version)); } } + + void dumpContent() { + if (cache != null && LOGGER_CONTENT.isInfoEnabled()) { + cache.invokeAll(cache.keys(), e -> { + LOGGER_CONTENT.info("Cached version: {}: {} (cached {} ms ago)", e.getKey(), e.getValue(), e.getValue().getAge()); + return null; + }); + } + } } diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalObjectCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalObjectCache.java index d62f0418194..ea959110a17 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalObjectCache.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalObjectCache.java @@ -9,6 +9,8 @@ import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.util.caching.AbstractThreadLocalCache; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import java.util.Map; @@ -19,6 +21,8 @@ */ public class LocalObjectCache extends AbstractThreadLocalCache { + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(LocalObjectCache.class.getName() + ".content"); + private final Map> data = new ConcurrentHashMap<>(); public PrismObject get(String oid) { @@ -42,4 +46,10 @@ public String description() { protected int getSize() { return data.size(); } + + public void dumpContent(String threadName) { + if (LOGGER_CONTENT.isInfoEnabled()) { + data.forEach((k, v) -> LOGGER_CONTENT.info("Cached object [{}] {}: {}", threadName, k, v)); + } + } } diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalQueryCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalQueryCache.java index 68debd8eac5..cba25e2edbe 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalQueryCache.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalQueryCache.java @@ -9,6 +9,10 @@ import com.evolveum.midpoint.schema.SearchResultList; import com.evolveum.midpoint.util.caching.AbstractThreadLocalCache; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; + +import org.jetbrains.annotations.NotNull; import java.util.Iterator; import java.util.Map; @@ -19,13 +23,15 @@ */ public class LocalQueryCache extends AbstractThreadLocalCache { + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(LocalQueryCache.class.getName() + ".content"); + private final Map data = new ConcurrentHashMap<>(); public SearchResultList get(QueryKey key) { return data.get(key); } - public void put(QueryKey key, SearchResultList objects) { + public void put(QueryKey key, @NotNull SearchResultList objects) { data.put(key, objects); } @@ -43,7 +49,31 @@ protected int getSize() { return data.size(); } - public Iterator> getEntryIterator() { + public void dumpContent(String threadName) { + if (LOGGER_CONTENT.isInfoEnabled()) { + data.forEach((k, v) -> LOGGER_CONTENT.info("Cached query [{}] of {} ({} object(s)): {}: {}", threadName, + k.getType(), v.size(), k.getQuery(), v)); + } + } + + @SuppressWarnings("SameParameterValue") + static int getTotalCachedObjects(ConcurrentHashMap cacheInstances) { + int rv = 0; + for (LocalQueryCache cacheInstance : cacheInstances.values()) { + rv += cacheInstance.getCachedObjects(); + } + return rv; + } + + private int getCachedObjects() { + int rv = 0; + for (SearchResultList value : data.values()) { + rv += value.size(); + } + return rv; + } + + Iterator> getEntryIterator() { return data.entrySet().iterator(); } } diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalVersionCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalVersionCache.java index f5c8c6b323e..25a87ac5f13 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalVersionCache.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalVersionCache.java @@ -8,6 +8,8 @@ package com.evolveum.midpoint.repo.cache; import com.evolveum.midpoint.util.caching.AbstractThreadLocalCache; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -17,6 +19,8 @@ */ public class LocalVersionCache extends AbstractThreadLocalCache { + private static final Trace LOGGER_CONTENT = TraceManager.getTrace(LocalVersionCache.class.getName() + ".content"); + private final Map data = new ConcurrentHashMap<>(); public String get(String oid) { @@ -40,4 +44,10 @@ public String description() { protected int getSize() { return data.size(); } + + public void dumpContent(String threadName) { + if (LOGGER_CONTENT.isInfoEnabled()) { + data.forEach((k, v) -> LOGGER_CONTENT.info("Cached version [{}] {}: {}", threadName, k, v)); + } + } } diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/QueryKey.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/QueryKey.java index f4f4a00f526..807e230dd08 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/QueryKey.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/QueryKey.java @@ -10,6 +10,8 @@ import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import org.jetbrains.annotations.NotNull; + import java.util.Objects; /** @@ -18,11 +20,11 @@ */ public class QueryKey { - private final Class type; + @NotNull private final Class type; private final ObjectQuery query; private Integer cachedHashCode; - QueryKey(Class type, ObjectQuery query) { + QueryKey(@NotNull Class type, ObjectQuery query) { this.type = type; this.query = query != null ? query.clone() : null; } @@ -48,7 +50,7 @@ public int hashCode() { return cachedHashCode; } - public Class getType() { + @NotNull public Class getType() { return type; } @@ -59,7 +61,7 @@ public ObjectQuery getQuery() { @Override public String toString() { return "QueryKey{" + - "type=" + type + + "type=" + type.getSimpleName() + ", query=" + query + '}'; } diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java index d47f5b524dd..d283ff1a244 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java @@ -329,7 +329,7 @@ public PrismObject getObject(Class type, String oid } objectToReturn = loadAndCacheObject(type, oid, options, readOnly, localObjectsCache, local.supports, result); } else { - cacheObject.setTimeToLive(System.currentTimeMillis() + getTimeToVersionCheck(global.typeConfig, + cacheObject.setTimeToCheckVersion(System.currentTimeMillis() + getTimeToVersionCheck(global.typeConfig, global.cacheConfig)); // version matches, renew ttl collector.registerWeakHit(GlobalObjectCache.class, type, global.statisticsLevel); log("Cache (global): HIT with version check - getObject {} ({})", global.traceMiss, oid, @@ -1212,7 +1212,7 @@ private void clearQueryResultsGlobally(Class type, Str globalQueryCache.invokeAll(entry -> { QueryKey queryKey = entry.getKey(); all.incrementAndGet(); - if (change.mayAffect(queryKey, entry.getValue(), matchingRuleRegistry)) { + if (change.mayAffect(queryKey, entry.getValue().getResult(), matchingRuleRegistry)) { LOGGER.trace("Removing (from global cache) query for type={}, change={}: {}", type, change, queryKey.getQuery()); entry.remove(); removed.incrementAndGet(); @@ -1721,7 +1721,7 @@ private boolean hasVersionChanged(Class objectType, String } private boolean shouldCheckVersion(GlobalCacheObjectValue object) { - return object.getTimeToLive() < System.currentTimeMillis(); + return object.getTimeToCheckVersion() < System.currentTimeMillis(); } private PrismObject loadAndCacheObject(Class objectClass, String oid, @@ -1917,15 +1917,26 @@ public Collection getStateInformation() { rv.add(new SingleCacheStateInformationType(prismContext) .name(LocalObjectCache.class.getName()) .size(LocalObjectCache.getTotalSize(LOCAL_OBJECT_CACHE_INSTANCE))); - rv.add(new SingleCacheStateInformationType(prismContext) - .name(LocalQueryCache.class.getName()) - .size(LocalQueryCache.getTotalSize(LOCAL_QUERY_CACHE_INSTANCE))); rv.add(new SingleCacheStateInformationType(prismContext) .name(LocalVersionCache.class.getName()) .size(LocalVersionCache.getTotalSize(LOCAL_VERSION_CACHE_INSTANCE))); + rv.add(new SingleCacheStateInformationType(prismContext) + .name(LocalQueryCache.class.getName()) + .size(LocalQueryCache.getTotalSize(LOCAL_QUERY_CACHE_INSTANCE)) + .secondarySize(LocalQueryCache.getTotalCachedObjects(LOCAL_QUERY_CACHE_INSTANCE))); rv.addAll(globalObjectCache.getStateInformation()); rv.addAll(globalVersionCache.getStateInformation()); rv.addAll(globalQueryCache.getStateInformation()); return rv; } + + @Override + public void dumpContent() { + LocalObjectCache.dumpContent(LOCAL_OBJECT_CACHE_INSTANCE); + LocalVersionCache.dumpContent(LOCAL_VERSION_CACHE_INSTANCE); + LocalQueryCache.dumpContent(LOCAL_QUERY_CACHE_INSTANCE); + globalObjectCache.dumpContent(); + globalVersionCache.dumpContent(); + globalQueryCache.dumpContent(); + } } diff --git a/repo/repo-cache/src/test/java/com/evolveum/midpoint/repo/cache/CacheInvalidationPerformanceTest.java b/repo/repo-cache/src/test/java/com/evolveum/midpoint/repo/cache/CacheInvalidationPerformanceTest.java new file mode 100644 index 00000000000..984299a213e --- /dev/null +++ b/repo/repo-cache/src/test/java/com/evolveum/midpoint/repo/cache/CacheInvalidationPerformanceTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2010-2019 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.cache; + +import static com.evolveum.midpoint.prism.util.PrismTestUtil.displayCollection; + +import static com.evolveum.midpoint.prism.util.PrismTestUtil.getPrismContext; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import javax.annotation.PostConstruct; + +import com.evolveum.midpoint.prism.delta.ItemDelta; +import com.evolveum.midpoint.prism.polystring.PolyString; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.testng.annotations.BeforeSuite; +import org.testng.annotations.Test; +import org.xml.sax.SAXException; + +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.prism.util.PrismTestUtil; +import com.evolveum.midpoint.schema.*; +import com.evolveum.midpoint.schema.constants.MidPointConstants; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.test.util.AbstractSpringTest; +import com.evolveum.midpoint.test.util.InfraTestMixin; +import com.evolveum.midpoint.util.PrettyPrinter; +import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; + +/** + * Currently not a part of automated test suite. + */ +@ContextConfiguration(locations = { "classpath:ctx-repo-cache-test.xml" }) +public class CacheInvalidationPerformanceTest extends AbstractSpringTest implements InfraTestMixin { + + private static final String CLASS_DOT = CacheInvalidationPerformanceTest.class.getName() + "."; + + @Autowired RepositoryCache repositoryCache; + + @BeforeSuite + public void setup() throws SchemaException, SAXException, IOException { + PrettyPrinter.setDefaultNamespacePrefix(MidPointConstants.NS_MIDPOINT_PUBLIC_PREFIX); + PrismTestUtil.resetPrismContext(MidPointPrismContextFactory.FACTORY); + } + + @PostConstruct + public void initialize() throws SchemaException, ObjectAlreadyExistsException { + OperationResult initResult = new OperationResult(CLASS_DOT + "setup"); + repositoryCache.postInit(initResult); + } + + @Test + public void test100InvalidationPerformance() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + + final int CACHED_SEARCHES = 10000; + + given(); + OperationResult result = createOperationResult(); + + // create the archetype - we should create reasonably sized object, as + ArchetypeType archetype = new ArchetypeType(getPrismContext()) + .name("name-initial") + .displayName("some display name") + .locality("some locality") + .costCenter("some cost center") + .beginActivation() + .administrativeStatus(ActivationStatusType.ENABLED) + .end(); + repositoryCache.addObject(archetype.asPrismObject(), null, result); + + modifyArchetypeName(archetype, "name-intermediate", "Initial modification duration", result); + modifyArchetypeName(archetype, "name", "Initial modification duration (repeated)", result); + + // fill-in cache with queries + for (int i = 0; i < CACHED_SEARCHES; i++) { + ObjectQuery query = getPrismContext().queryFor(ArchetypeType.class) + .item(ArchetypeType.F_NAME).eq(PolyString.fromOrig("name-" + i)).matchingOrig() + .or().item(ArchetypeType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS).eq(ActivationStatusType.ARCHIVED) + .or().item(ArchetypeType.F_COST_CENTER).eq("cc100").matchingCaseIgnore() + .build(); + repositoryCache.searchObjects(ArchetypeType.class, query, null, result); + } + + repositoryCache.dumpContent(); + Collection stateInformation = repositoryCache.getStateInformation(); + displayCollection("cache state information", stateInformation); + + when(); + modifyArchetypeName(archetype, "name-0", "Second modification duration (with cached searches)", result); + + then(); + } + + private void modifyArchetypeName(ArchetypeType archetype, String name, String label, OperationResult result) + throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + List> itemDeltas = getPrismContext().deltaFor(ArchetypeType.class) + .item(ArchetypeType.F_NAME) + .replace(PolyString.fromOrig(name)) + .asItemDeltas(); + + long start = System.currentTimeMillis(); + repositoryCache.modifyObject(ArchetypeType.class, archetype.getOid(), itemDeltas, result); + long duration = System.currentTimeMillis() - start; + displayValue(label, duration); + } +} diff --git a/repo/repo-cache/src/test/resources/logback-test.xml b/repo/repo-cache/src/test/resources/logback-test.xml index cde591929fc..2163ef393d1 100644 --- a/repo/repo-cache/src/test/resources/logback-test.xml +++ b/repo/repo-cache/src/test/resources/logback-test.xml @@ -15,7 +15,7 @@ - + diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java index b1e4896040a..b3327950b81 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java @@ -66,4 +66,9 @@ public void invalidate(Class type, String oid, CacheInvalidationContext conte public Collection getStateInformation() { return Collections.emptySet(); } + + @Override + public void dumpContent() { + // nothing to do here + } } diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionFactory.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionFactory.java index 3cfbacc5066..a5fc736084f 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionFactory.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionFactory.java @@ -210,4 +210,9 @@ public Collection getStateInformation() { .size(cache.size()) ); } + + @Override + public void dumpContent() { + // Implement eventually + } } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractHigherUnitTest.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractHigherUnitTest.java index 491f25722f0..db46faf68f0 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractHigherUnitTest.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractHigherUnitTest.java @@ -229,8 +229,8 @@ public QName getName() { } @Override - public boolean isSupported(QName xsdType) { - return nameMatchingRule.isSupported(xsdType); + public boolean supports(QName xsdType) { + return nameMatchingRule.supports(xsdType); } @Override diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java index 4934230d87d..53f1dac46f0 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java @@ -964,8 +964,8 @@ public QName getName() { } @Override - public boolean isSupported(QName xsdType) { - return nameMatchingRule.isSupported(xsdType); + public boolean supports(QName xsdType) { + return nameMatchingRule.supports(xsdType); } @Override From da84ac86746bcb14ef47b7fb450e82a7c7197523 Mon Sep 17 00:00:00 2001 From: lskublik Date: Mon, 27 Apr 2020 17:39:40 +0200 Subject: [PATCH 3/6] adding schrodinger tests for next LABs of demo 101 (MODULE 10) --- .../schrodinger/labs/AbstractLabTest.java | 18 +- .../schrodinger/labs/M10ObjectTemplate.java | 335 +++++++++++ .../M3ResourcesAttributesAndMappingsTest.java | 5 +- .../labs/M4ProvisioningToResources.java | 8 +- .../labs/M5AccountsAssignmentsAndRoles.java | 12 +- .../M6ConfiguringMultipleAccountTypes.java | 4 +- .../labs/M7SynchronizationFlavours.java | 40 +- .../labs/M8ExtendingMidPointXMLSchema.java | 5 +- .../labs/M9OrganizationalStructure.java | 31 +- .../schrodinger/page/TaskPageTest.java | 4 +- .../lookupTables/lookup-emp-status.xml | 27 + .../object-template-example-user-10-3.xml | 247 ++++++++ .../object-template-example-user-simple.xml | 36 ++ .../object-template-example-user.xml | 249 ++++++++ .../localhost-csvfile-3-ldap-10-4.xml | 537 ++++++++++++++++++ .../localhost-csvfile-3-ldap-4-4.xml | 1 - .../resources/labs/sources/source-10-1.csv | 17 + .../labs/sources/source-10-2-part1.csv | 17 + .../labs/sources/source-10-2-part2.csv | 17 + .../labs/sources/source-10-2-part3.csv | 17 + .../schrodingertest/testng-integration.xml | 1 + .../schrodinger/component/AssignmentsTab.java | 19 + .../component/common/PrismForm.java | 6 + .../common/PrismFormWithActionButtons.java | 4 +- .../configuration/ObjectPolicyTab.java | 81 +++ .../component/configuration/SystemTab.java | 8 + .../component/org/ManagerPanel.java | 38 ++ .../schrodinger/component/org/OrgRootTab.java | 5 + .../table/TableHeaderDropDownMenu.java | 12 + .../component/user/ProjectionsDropDown.java | 20 +- .../midpoint/schrodinger/page/BasicPage.java | 5 + .../page/configuration/SystemPage.java | 14 +- 32 files changed, 1743 insertions(+), 97 deletions(-) create mode 100644 testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M10ObjectTemplate.java create mode 100644 testing/schrodingertest/src/test/resources/labs/objects/lookupTables/lookup-emp-status.xml create mode 100644 testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user-10-3.xml create mode 100644 testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user-simple.xml create mode 100644 testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user.xml create mode 100644 testing/schrodingertest/src/test/resources/labs/objects/resources/localhost-csvfile-3-ldap-10-4.xml create mode 100644 testing/schrodingertest/src/test/resources/labs/sources/source-10-1.csv create mode 100644 testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part1.csv create mode 100644 testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part2.csv create mode 100644 testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part3.csv create mode 100644 tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/configuration/ObjectPolicyTab.java create mode 100644 tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/org/ManagerPanel.java diff --git a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/AbstractLabTest.java b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/AbstractLabTest.java index 6a3cd6bf98a..4a7af6e4d4f 100644 --- a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/AbstractLabTest.java +++ b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/AbstractLabTest.java @@ -12,13 +12,12 @@ import com.evolveum.midpoint.schrodinger.component.assignmentholder.AssignmentHolderObjectListTable; import com.evolveum.midpoint.schrodinger.component.resource.ResourceAccountsTab; import com.evolveum.midpoint.schrodinger.component.resource.ResourceShadowTable; -import com.evolveum.midpoint.schrodinger.page.configuration.AboutPage; import com.evolveum.midpoint.schrodinger.page.resource.AccountPage; import com.evolveum.midpoint.schrodinger.page.resource.ViewResourcePage; +import com.evolveum.midpoint.schrodinger.page.task.TaskPage; import com.evolveum.midpoint.schrodinger.page.user.ListUsersPage; import com.evolveum.midpoint.schrodinger.page.user.UserPage; import com.evolveum.midpoint.testing.schrodinger.AbstractSchrodingerTest; -import com.evolveum.midpoint.testing.schrodinger.scenarios.SynchronizationTests; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,6 +55,10 @@ public class AbstractLabTest extends AbstractSchrodingerTest { protected static final File HR_SOURCE_FILE_7_4_PART_2 = new File(LAB_SOURCES_DIRECTORY + "source-7-4-part-2.csv"); protected static final File HR_SOURCE_FILE_7_4_PART_3 = new File(LAB_SOURCES_DIRECTORY + "source-7-4-part-3.csv"); protected static final File HR_SOURCE_FILE_7_4_PART_4 = new File(LAB_SOURCES_DIRECTORY + "source-7-4-part-4.csv"); + protected static final File HR_SOURCE_FILE_10_1 = new File(LAB_SOURCES_DIRECTORY + "source-10-1.csv"); + protected static final File HR_SOURCE_FILE_10_2_PART1 = new File(LAB_SOURCES_DIRECTORY + "source-10-2-part1.csv"); + protected static final File HR_SOURCE_FILE_10_2_PART2 = new File(LAB_SOURCES_DIRECTORY + "source-10-2-part2.csv"); + protected static final File HR_SOURCE_FILE_10_2_PART3 = new File(LAB_SOURCES_DIRECTORY + "source-10-2-part3.csv"); protected static final String DIRECTORY_CURRENT_TEST = "labTests"; @@ -162,4 +165,15 @@ public ResourceShadowTable getShadowTable(String resourceName, String searchedIt .and() .and(); } + + protected TaskPage showTask(String name) { + return basicPage.listTasks() + .table() + .search() + .byName() + .inputValue(name) + .updateSearch() + .and() + .clickByName(name); + } } diff --git a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M10ObjectTemplate.java b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M10ObjectTemplate.java new file mode 100644 index 00000000000..cb7690fa5ec --- /dev/null +++ b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M10ObjectTemplate.java @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2010-2019 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.schrodinger.labs; + +import com.codeborne.selenide.Selenide; + +import com.codeborne.selenide.ex.ElementNotFound; + +import com.evolveum.midpoint.schrodinger.MidPoint; +import com.evolveum.midpoint.schrodinger.component.AssignmentHolderBasicTab; +import com.evolveum.midpoint.schrodinger.component.AssignmentsTab; +import com.evolveum.midpoint.schrodinger.component.common.PrismForm; +import com.evolveum.midpoint.schrodinger.component.common.PrismFormWithActionButtons; +import com.evolveum.midpoint.schrodinger.component.configuration.ObjectPolicyTab; +import com.evolveum.midpoint.schrodinger.component.org.OrgRootTab; +import com.evolveum.midpoint.schrodinger.component.resource.ResourceAccountsTab; +import com.evolveum.midpoint.schrodinger.page.configuration.AboutPage; +import com.evolveum.midpoint.schrodinger.page.resource.ViewResourcePage; +import com.evolveum.midpoint.schrodinger.page.task.TaskPage; +import com.evolveum.midpoint.schrodinger.page.user.UserPage; +import com.evolveum.midpoint.testing.schrodinger.scenarios.ScenariosCommons; + +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; + +/** + * @author skublik + */ + +public class M10ObjectTemplate extends AbstractLabTest{ + + private static final Logger LOG = LoggerFactory.getLogger(M10ObjectTemplate.class); + + private static final File OBJECT_TEMPLATE_USER_SIMPLE_FILE = new File(LAB_OBJECTS_DIRECTORY + "objectTemplate/object-template-example-user-simple.xml"); + private static final File OBJECT_TEMPLATE_USER_FILE = new File(LAB_OBJECTS_DIRECTORY + "objectTemplate/object-template-example-user.xml"); + private static final File OBJECT_TEMPLATE_USER_FILE_10_3 = new File(LAB_OBJECTS_DIRECTORY + "objectTemplate/object-template-example-user-10-3.xml"); + private static final File LOOKUP_EMP_STATUS_FILE = new File(LAB_OBJECTS_DIRECTORY + "lookupTables/lookup-emp-status.xml"); + private static final File CSV_3_RESOURCE_FILE_10_4 = new File(LAB_OBJECTS_DIRECTORY + "resources/localhost-csvfile-3-ldap-10-4.xml"); + + @AfterClass + @Override + public void afterClass() { + super.afterClass(); + + midPoint.formLogin().loginWithReloadLoginPage(username, password); + + LOG.info("After: Login name " + username + " pass " + password); + + AboutPage aboutPage = basicPage.aboutPage(); + aboutPage + .clickSwitchToFactoryDefaults() + .clickYes(); + } + + @Test(groups={"M10"}, dependsOnGroups={"M9"}) + public void mod10test01SimpleObjectTemplate() throws IOException { + importObject(OBJECT_TEMPLATE_USER_SIMPLE_FILE, true); + + ((PrismFormWithActionButtons)basicPage.objectPolicy() + .clickAddObjectPolicy() + .selectOption("type", "User") + .editRefValue("objectTemplateRef") + .table() + .clickByName("ExAmPLE User Template")) + .clickDone() + .and() + .save() + .feedback() + .isSuccess(); + + showUser("X001212") + .checkReconcile() + .clickSave() + .feedback() + .isSuccess(); + + Assert.assertTrue(showUser("X001212") + .selectTabBasic() + .form() + .compareInputAttributeValue("fullName", "John Smith")); + + showTask("HR Synchronization").clickResume(); + + FileUtils.copyFile(HR_SOURCE_FILE_10_1, hrTargetFile); + Selenide.sleep(MidPoint.TIMEOUT_MEDIUM_6_S); + + Assert.assertTrue(showUser("X000998") + .selectTabBasic() + .form() + .compareInputAttributeValue("fullName", "David Lister")); + + TaskPage task = basicPage.newTask(); + task.setHandlerUriForNewTask("Recompute task"); + task.selectTabBasic() + .form() + .addAttributeValue("name", "User Recomputation Task") + .selectOption("recurrence","Single") + .selectOption("objectType","User") + .and() + .and() + .clickSaveAndRun() + .feedback() + .isInfo(); + + Assert.assertTrue(showUser("kirk") + .selectTabBasic() + .form() + .compareInputAttributeValue("fullName", "Jim Tiberius Kirk")); + } + + @Test(dependsOnMethods = {"mod10test01SimpleObjectTemplate"}, groups={"M10"}, dependsOnGroups={"M9"}) + public void mod10test02AutomaticAssignments() throws IOException { + importObject(OBJECT_TEMPLATE_USER_FILE, true); + + ResourceAccountsTab accountTab = basicPage.listResources() + .table() + .clickByName(HR_RESOURCE_NAME) + .clickAccountsTab() + .clickSearchInResource(); + Selenide.sleep(MidPoint.TIMEOUT_DEFAULT_2_S); + accountTab.table() + .selectCheckboxByName("001212") + .clickHeaderActionDropDown() + .clickImport() + .and() + .and() + .feedback() + .isSuccess(); + + AssignmentsTab tab = accountTab.table() + .clickOnOwnerByName("X001212") + .selectTabAssignments(); + + Assert.assertTrue(tab.containsAssignmentsWithRelation("Default", "Human Resources", + "Active Employees", "Internal Employee")); + Assert.assertTrue(tab.containsAssignmentsWithRelation("Manager", "Human Resources")); + + FileUtils.copyFile(HR_SOURCE_FILE_10_2_PART1, hrTargetFile); + Selenide.sleep(MidPoint.TIMEOUT_MEDIUM_6_S); + + Assert.assertTrue(showUser("X000999") + .selectTabAssignments() + .containsAssignmentsWithRelation("Default", "Java Development", + "Active Employees", "Internal Employee")); + + showTask("User Recomputation Task").clickRunNow(); + Selenide.sleep(MidPoint.TIMEOUT_MEDIUM_6_S); + + Assert.assertTrue(showUser("X000998") + .selectTabAssignments() + .containsAssignmentsWithRelation("Default", "Java Development", + "Active Employees", "Internal Employee")); + + FileUtils.copyFile(HR_SOURCE_FILE_10_2_PART2, hrTargetFile); + Selenide.sleep(MidPoint.TIMEOUT_MEDIUM_6_S); + + UserPage user = showUser("X000998"); + Assert.assertTrue(user.selectTabBasic() + .form() + .compareSelectAttributeValue("administrativeStatus", "Disabled")); + Assert.assertTrue(user.selectTabAssignments() + .containsAssignmentsWithRelation("Default", "Inactive Employees", "Internal Employee")); + + FileUtils.copyFile(HR_SOURCE_FILE_10_2_PART3, hrTargetFile); + Selenide.sleep(MidPoint.TIMEOUT_MEDIUM_6_S); + + user = showUser("X000998"); + Assert.assertTrue(user.selectTabBasic() + .form() + .compareSelectAttributeValue("administrativeStatus", "Disabled")); + Assert.assertTrue(user.selectTabAssignments() + .containsAssignmentsWithRelation("Default", "Former Employees")); + + FileUtils.copyFile(HR_SOURCE_FILE_10_2_PART1, hrTargetFile); + Selenide.sleep(MidPoint.TIMEOUT_MEDIUM_6_S); + + user = showUser("X000998"); + Assert.assertTrue(user.selectTabBasic() + .form() + .compareSelectAttributeValue("administrativeStatus", "Enabled")); + Assert.assertTrue(showUser("X000998") + .selectTabAssignments() + .containsAssignmentsWithRelation("Default", "Java Development", + "Active Employees", "Internal Employee")); + } + + @Test(dependsOnMethods = {"mod10test02AutomaticAssignments"}, groups={"M10"}, dependsOnGroups={"M9"}) + public void mod10test03LookupTablesAndAttributeOverrides() { + + PrismForm> form = showUser("kirk") + .selectTabBasic() + .form(); + + form.showEmptyAttributes("Properties"); + form.addAttributeValue("empStatus", "O"); + form.addAttributeValue("familyName", "kirk2"); + boolean existFeedback = false; + try { existFeedback = form.and().and().feedback().isError(); } catch (ElementNotFound e) { } + Assert.assertFalse(existFeedback); + Assert.assertTrue(form.findProperty("telephoneNumber"). + $x(".//i[contains(@data-original-title, 'Primary telephone number of the user, org. unit, etc.')]").exists()); + Assert.assertFalse(form.findProperty("telephoneNumber"). + $x(".//i[contains(@data-original-title, 'Mobile Telephone Number')]").exists()); + Assert.assertTrue(form.isPropertyEnabled("honorificSuffix")); + + importObject(LOOKUP_EMP_STATUS_FILE, true); + importObject(OBJECT_TEMPLATE_USER_FILE_10_3, true); + + form = showUser("kirk") + .selectTabBasic() + .form(); + + form.showEmptyAttributes("Properties"); + form.addAttributeValue("empStatus", "O"); + form.addAttributeValue("familyName", "kirk2"); + Assert.assertTrue(form.and().and().feedback().isError()); + Assert.assertFalse(form.findProperty("telephoneNumber"). + $x(".//i[contains(@data-original-title, 'Primary telephone number of the user, org. unit, etc.')]").exists()); + Assert.assertTrue(form.findProperty("telephoneNumber"). + $x(".//i[contains(@data-original-title, 'Mobile Telephone Number')]").exists()); + Assert.assertFalse(form.isPropertyEnabled("honorificSuffix")); + } + + @Test(dependsOnMethods = {"mod10test03LookupTablesAndAttributeOverrides"}, groups={"M10"}, dependsOnGroups={"M9"}) + public void mod10test04FinishingManagerMapping() { + Selenide.sleep(MidPoint.TIMEOUT_MEDIUM_6_S); + showTask("User Recomputation Task").clickRunNow(); + Selenide.sleep(MidPoint.TIMEOUT_MEDIUM_6_S); + + OrgRootTab rootTab = basicPage.orgStructure() + .selectTabWithRootOrg("ExAmPLE, Inc. - Functional Structure"); + Assert.assertTrue(rootTab.getOrgHierarchyPanel() + .expandAllOrgs() + .selectOrgInTree("IT Administration Department") + .and() + .getManagerPanel() + .containsManager("John Wicks")); + + rootTab.getMemberPanel() + .selectType("User") + .table() + .clickByName("X000158"); + Assert.assertTrue(new UserPage().selectTabProjections() + .table() + .clickByName("cn=Alice Black,ou=0212,ou=0200,ou=ExAmPLE,dc=example,dc=com") + .compareInputAttributeValue("manager", "X000390")); + Assert.assertTrue(showUser("X000390").selectTabProjections() + .table() + .clickByName("cn=John Wicks,ou=0212,ou=0200,ou=ExAmPLE,dc=example,dc=com") + .compareInputAttributeValue("manager", "X000035")); + Assert.assertTrue(showUser("X000035").selectTabProjections() + .table() + .clickByName("cn=James Bradley,ou=0200,ou=ExAmPLE,dc=example,dc=com") + .showEmptyAttributes("Attributes") + .compareInputAttributeValue("manager", "")); + + Assert.assertTrue(showUser("kirk") + .selectTabAssignments() + .containsAssignmentsWithRelation("Default", "Warp Speed Research")); + Assert.assertTrue(new UserPage().selectTabProjections() + .table() + .clickByName("cn=Jim Tiberius Kirk,ou=ExAmPLE,dc=example,dc=com") + .showEmptyAttributes("Attributes") + .compareInputAttributeValue("manager", "")); + + showUser("picard") + .selectTabAssignments() + .clickAddAssignemnt("New Organization type assignment with manager relation") + .selectType("Org") + .table() + .search() + .byName() + .inputValue("0919") + .updateSearch() + .and() + .selectCheckboxByName("0919") + .and() + .clickAdd() + .and() + .clickSave() + .feedback() + .isSuccess(); + + showUser("kirk").checkReconcile() + .clickSave() + .feedback() + .isSuccess(); + + Assert.assertTrue(showUser("kirk").selectTabProjections() + .table() + .clickByName("cn=Jim Tiberius Kirk,ou=ExAmPLE,dc=example,dc=com") + .compareInputAttributeValue("manager", "picard")); + + showUser("picard").selectTabAssignments() + .table() + .selectCheckboxByName("Warp Speed Research") + .removeByName("Warp Speed Research") + .and() + .and() + .clickSave() + .feedback() + .isSuccess(); + + Assert.assertTrue(showUser("kirk").selectTabProjections() + .table() + .clickByName("cn=Jim Tiberius Kirk,ou=ExAmPLE,dc=example,dc=com") + .compareInputAttributeValue("manager", "picard")); + + importObject(CSV_3_RESOURCE_FILE_10_4,true); + changeResourceAttribute(CSV_3_RESOURCE_NAME, ScenariosCommons.CSV_RESOURCE_ATTR_FILE_PATH, csv3TargetFile.getAbsolutePath(), true); + + showUser("kirk").checkReconcile() + .clickSave() + .feedback() + .isSuccess(); + + Assert.assertTrue(showUser("kirk").selectTabProjections() + .table() + .clickByName("cn=Jim Tiberius Kirk,ou=ExAmPLE,dc=example,dc=com") + .showEmptyAttributes("Attributes") + .compareInputAttributeValue("manager", "")); + } + +} diff --git a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M3ResourcesAttributesAndMappingsTest.java b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M3ResourcesAttributesAndMappingsTest.java index 545e3e7d1d2..2b10a501036 100644 --- a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M3ResourcesAttributesAndMappingsTest.java +++ b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M3ResourcesAttributesAndMappingsTest.java @@ -14,7 +14,6 @@ import com.evolveum.midpoint.schema.SchemaConstantsGenerated; import com.evolveum.midpoint.schrodinger.MidPoint; import com.evolveum.midpoint.schrodinger.component.common.PrismForm; -import com.evolveum.midpoint.schrodinger.component.resource.ResourceShadowTable; import com.evolveum.midpoint.schrodinger.page.resource.AccountPage; import com.evolveum.midpoint.schrodinger.page.resource.ListResourcesPage; import com.evolveum.midpoint.schrodinger.page.resource.ResourceWizardPage; @@ -50,7 +49,7 @@ public void beforeClass() throws IOException { } @Test(groups={"M3"}) - public void test0301ViewingResources() throws Exception { + public void mod03test01ViewingResources() throws Exception { initTestDirectory(DIRECTORY_CURRENT_TEST); csv1TargetFile = new File(csvTargetDir, CSV_1_FILE_SOURCE_NAME); @@ -144,7 +143,7 @@ public void test0301ViewingResources() throws Exception { } @Test(dependsOnMethods = {"test0301ViewingResources"}, groups={"M3"}) - public void test0302BasicProvisioning() { + public void mod03test02BasicProvisioning() { UserPage user = basicPage.newUser(); user.selectTabBasic() .form() diff --git a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M4ProvisioningToResources.java b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M4ProvisioningToResources.java index d427bf81079..f1a8fba5bc9 100644 --- a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M4ProvisioningToResources.java +++ b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M4ProvisioningToResources.java @@ -46,7 +46,7 @@ public class M4ProvisioningToResources extends AbstractLabTest { @Test(groups={"M4"}, dependsOnGroups={"M3"}) - public void test0401BasicProvisioningToMultipleResources() { + public void mod04test01BasicProvisioningToMultipleResources() { importObject(CSV_1_RESOURCE_FILE,true); changeResourceAttribute(CSV_1_RESOURCE_NAME, ScenariosCommons.CSV_RESOURCE_ATTR_FILE_PATH, csv1TargetFile.getAbsolutePath(), true); @@ -182,7 +182,7 @@ public void test0401BasicProvisioningToMultipleResources() { } @Test(dependsOnMethods = {"test0401BasicProvisioningToMultipleResources"}, groups={"M4"}, dependsOnGroups={"M3"}) - public void test0402AddingMappings() { + public void mod04test02AddingMappings() { importObject(CSV_1_RESOURCE_FILE_4_2,true); changeResourceAttribute(CSV_1_RESOURCE_NAME, ScenariosCommons.CSV_RESOURCE_ATTR_FILE_PATH, csv1TargetFile.getAbsolutePath(), true); @@ -217,7 +217,7 @@ public void test0402AddingMappings() { } @Test(dependsOnMethods = {"test0402AddingMappings"}, groups={"M4"}, dependsOnGroups={"M3"}) - public void test0403ModifyingExistingMappings() { + public void mod04test03ModifyingExistingMappings() { importObject(CSV_1_RESOURCE_FILE_4_3,true); changeResourceAttribute(CSV_1_RESOURCE_NAME, ScenariosCommons.CSV_RESOURCE_ATTR_FILE_PATH, csv1TargetFile.getAbsolutePath(), true); @@ -272,7 +272,7 @@ public void test0403ModifyingExistingMappings() { } @Test(dependsOnMethods = {"test0403ModifyingExistingMappings"}, groups={"M4"}, dependsOnGroups={"M3"}) - public void test0404AddingANewAttribute() { + public void mod04test04AddingANewAttribute() { ((PrismFormWithActionButtons>>) ((AbstractTableWithPrismView)showUser("kirk") .selectTabProjections() diff --git a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M5AccountsAssignmentsAndRoles.java b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M5AccountsAssignmentsAndRoles.java index 466da487dd3..29bc8d89e1e 100644 --- a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M5AccountsAssignmentsAndRoles.java +++ b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M5AccountsAssignmentsAndRoles.java @@ -61,7 +61,7 @@ public class M5AccountsAssignmentsAndRoles extends AbstractLabTest { private static final File SYSTEM_CONFIGURATION_FILE_5_7 = new File(LAB_OBJECTS_DIRECTORY + "systemConfiguration/system-configuration-5-7.xml"); @Test(groups={"M5"}, dependsOnGroups={"M4"}) - public void test0501UsingRBAC() { + public void mod05test01UsingRBAC() { importObject(INTERNAL_EMPLOYEE_ROLE_FILE,true); importObject(INCOGNITO_ROLE_FILE,true); importObject(SECRET_I_ROLE_FILE,true); @@ -97,7 +97,7 @@ public void test0501UsingRBAC() { } @Test(dependsOnMethods = {"test0501UsingRBAC"}, groups={"M5"}, dependsOnGroups={"M4"}) - public void test0502SegregationOfDuties() { + public void mod05test02SegregationOfDuties() { showUser("kirk").selectTabAssignments() .clickAddAssignemnt() .table() @@ -116,7 +116,7 @@ public void test0502SegregationOfDuties() { } @Test(dependsOnMethods = {"test0502SegregationOfDuties"}, groups={"M5"}, dependsOnGroups={"M4"}) - public void test0504CreatingRoles() { + public void mod05test04CreatingRoles() { InducementsTab tab = basicPage.newRole() .selectTabBasic() .form() @@ -141,7 +141,7 @@ public void test0504CreatingRoles() { } @Test(dependsOnMethods = {"test0504CreatingRoles"}, groups={"M5"}, dependsOnGroups={"M4"}) - public void test0505DisableOnUnassign() { + public void mod05test05DisableOnUnassign() { importObject(CSV_1_RESOURCE_FILE_5_5,true); changeResourceAttribute(CSV_1_RESOURCE_NAME, ScenariosCommons.CSV_RESOURCE_ATTR_FILE_PATH, csv1TargetFile.getAbsolutePath(), true); @@ -181,7 +181,7 @@ public void test0505DisableOnUnassign() { } @Test(dependsOnMethods = {"test0505DisableOnUnassign"}, groups={"M5"}, dependsOnGroups={"M4"}) - public void test0506InactiveAssignment() { + public void mod05test06InactiveAssignment() { Utils.addAsignments(showUser("kirk").selectTabAssignments(), "Too Many Secrets"); AccountPage shadow = showShadow(CSV_1_RESOURCE_NAME, "Login", "jkirk"); Selenide.sleep(MidPoint.TIMEOUT_DEFAULT_2_S); @@ -227,7 +227,7 @@ public void test0506InactiveAssignment() { } @Test(dependsOnMethods = {"test0506InactiveAssignment"}, groups={"M5"}, dependsOnGroups={"M4"}) - public void test0507ArchetypesIntroduction() { + public void mod05test07ArchetypesIntroduction() { importObject(ARCHETYPE_EMPLOYEE_FILE, true); importObject(ARCHETYPE_EXTERNAL_FILE, true); diff --git a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M6ConfiguringMultipleAccountTypes.java b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M6ConfiguringMultipleAccountTypes.java index 8200bf44be5..b4a899c6dc0 100644 --- a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M6ConfiguringMultipleAccountTypes.java +++ b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M6ConfiguringMultipleAccountTypes.java @@ -15,11 +15,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.Assert; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.File; -import java.io.IOException; /** * @author skublik @@ -37,7 +35,7 @@ public class M6ConfiguringMultipleAccountTypes extends AbstractLabTest { private static final String CSV3_ADMIN_ROLE_NAME = "CSV-3 Admin"; @Test(groups={"M6"}, dependsOnGroups={"M5"}) - public void test0601UsingAccountIntentsForProvisioning() { + public void mod06test01UsingAccountIntentsForProvisioning() { importObject(CSV_1_RESOURCE_FILE_6_1,true); changeResourceAttribute(CSV_1_RESOURCE_NAME, ScenariosCommons.CSV_RESOURCE_ATTR_FILE_PATH, csv1TargetFile.getAbsolutePath(), true); diff --git a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M7SynchronizationFlavours.java b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M7SynchronizationFlavours.java index 3d55b3ff12e..9bdf203347c 100644 --- a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M7SynchronizationFlavours.java +++ b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M7SynchronizationFlavours.java @@ -35,7 +35,7 @@ public class M7SynchronizationFlavours extends AbstractLabTest{ private static final Logger LOG = LoggerFactory.getLogger(M7SynchronizationFlavours.class); @Test(groups={"M7"}, dependsOnGroups={"M6"}) - public void test0701RunningImportFromResource() throws IOException { + public void mod07test01RunningImportFromResource() throws IOException { hrTargetFile = new File(csvTargetDir, HR_FILE_SOURCE_NAME); FileUtils.copyFile(HR_SOURCE_FILE, hrTargetFile); @@ -79,16 +79,14 @@ public void test0701RunningImportFromResource() throws IOException { .feedback() .isInfo(); - Assert.assertEquals(basicPage.listTasks() - .table() - .clickByName("Initial import from HR") - .selectTabOperationStatistics() - .getSuccessfullyProcessed(), 14); + Assert.assertEquals(showTask("Initial import from HR") + .selectTabOperationStatistics() + .getSuccessfullyProcessed(), 14); Assert.assertEquals(basicPage.listUsers(ARCHETYPE_EMPLOYEE_PLURAL_LABEL).getCountOfObjects(), 15); } @Test(dependsOnMethods = {"test0701RunningImportFromResource"}, groups={"M7"}, dependsOnGroups={"M6"}) - public void test0702RunningAccountReconciliation() { + public void mod07test02RunningAccountReconciliation() { Selenide.sleep(MidPoint.TIMEOUT_MEDIUM_6_S); createReconTask("CSV-1 Reconciliation", CSV_1_RESOURCE_NAME); Selenide.sleep(MidPoint.TIMEOUT_SHORT_4_S); @@ -110,13 +108,10 @@ public void test0702RunningAccountReconciliation() { } @Test(dependsOnMethods = {"test0702RunningAccountReconciliation"}, groups={"M7"}, dependsOnGroups={"M6"}) - public void test0703RunningAttributeReconciliation() throws IOException { + public void mod07test03RunningAttributeReconciliation() throws IOException { FileUtils.copyFile(CSV_1_SOURCE_FILE_7_3, csv1TargetFile); - basicPage.listTasks() - .table() - .clickByName("CSV-1 Reconciliation") - .clickRunNow(); + showTask("CSV-1 Reconciliation").clickRunNow(); Assert.assertTrue( showShadow(CSV_1_RESOURCE_NAME, "Login", "jkirk") @@ -127,7 +122,7 @@ public void test0703RunningAttributeReconciliation() throws IOException { } @Test(dependsOnMethods = {"test0703RunningAttributeReconciliation"}, groups={"M7"}, dependsOnGroups={"M6"}) - public void test0704RunningLiveSync() throws IOException { + public void mod07test04RunningLiveSync() throws IOException { Selenide.sleep(MidPoint.TIMEOUT_MEDIUM_6_S); TaskPage task = basicPage.newTask(); task.setHandlerUriForNewTask("Live synchronization task"); @@ -233,16 +228,13 @@ private void createReconTask(String reconTaskName, String resource){ } private void deselectDryRun(String taskName) { - basicPage.listTasks() - .table() - .clickByName(taskName) - .selectTabBasic() - .form() - .selectOption("dryRun", "Undefined") - .and() - .and() - .clickSaveAndRun() - .feedback() - .isInfo(); + showTask(taskName).selectTabBasic() + .form() + .selectOption("dryRun", "Undefined") + .and() + .and() + .clickSaveAndRun() + .feedback() + .isInfo(); } } diff --git a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M8ExtendingMidPointXMLSchema.java b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M8ExtendingMidPointXMLSchema.java index 5479eeafe70..e4cd93d815c 100644 --- a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M8ExtendingMidPointXMLSchema.java +++ b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M8ExtendingMidPointXMLSchema.java @@ -20,7 +20,6 @@ import org.apache.commons.io.FileUtils; import org.testng.Assert; -import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -62,7 +61,7 @@ protected void springTestContextPrepareTestInstance() throws Exception { } @Test(groups={"M8"}, dependsOnGroups={"M7"}) - public void test0801ExtendingMidPointXMLSchema() { + public void mod08test01ExtendingMidPointXMLSchema() { PrismForm> form = basicPage.newUser() .selectTabBasic() .form(); @@ -72,6 +71,8 @@ public void test0801ExtendingMidPointXMLSchema() { form.findProperty("isManager"); form.findProperty("empStatus"); + showTask("HR Synchronization").clickSuspend(); + importObject(HR_RESOURCE_FILE_8_1,true); changeResourceAttribute(HR_RESOURCE_NAME, ScenariosCommons.CSV_RESOURCE_ATTR_FILE_PATH, hrTargetFile.getAbsolutePath(), true); diff --git a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M9OrganizationalStructure.java b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M9OrganizationalStructure.java index 29d5ba3948c..588a5e92672 100644 --- a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M9OrganizationalStructure.java +++ b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/labs/M9OrganizationalStructure.java @@ -6,24 +6,16 @@ */ package com.evolveum.midpoint.testing.schrodinger.labs; -import com.codeborne.selenide.Selenide; - -import com.evolveum.midpoint.schrodinger.page.configuration.AboutPage; import com.evolveum.midpoint.schrodinger.page.login.FormLoginPage; import com.evolveum.midpoint.schrodinger.page.org.OrgPage; import com.evolveum.midpoint.schrodinger.page.org.OrgTreePage; import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.File; -import java.io.IOException; /** * @author skublik @@ -31,8 +23,6 @@ public class M9OrganizationalStructure extends AbstractLabTest{ - private static final Logger LOG = LoggerFactory.getLogger(M9OrganizationalStructure.class); - private static final File ARCHETYPE_ORG_COMPANY_FILE = new File(LAB_OBJECTS_DIRECTORY + "archetypes/archetype-org-company.xml"); private static final File ARCHETYPE_ORG_FUNCTIONAL_FILE = new File(LAB_OBJECTS_DIRECTORY + "archetypes/archetype-org-functional.xml"); private static final File ARCHETYPE_ORG_GROUP_LIST_FILE = new File(LAB_OBJECTS_DIRECTORY + "archetypes/archetype-org-group-list.xml"); @@ -40,23 +30,8 @@ public class M9OrganizationalStructure extends AbstractLabTest{ private static final File ORG_EXAMPLE_FILE = new File(LAB_OBJECTS_DIRECTORY + "org/org-example.xml"); private static final File ORG_SECRET_OPS_FILE = new File(LAB_OBJECTS_DIRECTORY + "org/org-secret-ops.xml"); - @AfterClass - @Override - public void afterClass() { - super.afterClass(); - - midPoint.formLogin().loginWithReloadLoginPage(username, password); - - LOG.info("After: Login name " + username + " pass " + password); - - AboutPage aboutPage = basicPage.aboutPage(); - aboutPage - .clickSwitchToFactoryDefaults() - .clickYes(); - } - @Test(groups={"M9"}, dependsOnGroups={"M8"}) - public void test0901ImportStaticOrgStructure() { + public void mod09test01ImportStaticOrgStructure() { importObject(ARCHETYPE_ORG_FUNCTIONAL_FILE, true, true); importObject(ARCHETYPE_ORG_COMPANY_FILE, true); importObject(ARCHETYPE_ORG_GROUP_FILE, true); @@ -86,7 +61,7 @@ public void test0901ImportStaticOrgStructure() { } @Test(dependsOnMethods = {"test0901ImportStaticOrgStructure"}, groups={"M9"}, dependsOnGroups={"M8"}) - public void test0902CreateStaticOrgStructure() { + public void mod09test02CreateStaticOrgStructure() { basicPage.orgStructure() .selectTabWithRootOrg("ExAmPLE, Inc. - Functional Structure") .getOrgHierarchyPanel() @@ -157,7 +132,7 @@ public void test0902CreateStaticOrgStructure() { } @Test(dependsOnMethods = {"test0902CreateStaticOrgStructure"}, groups={"M9"}, dependsOnGroups={"M8"}) - public void test0903OrganizationActingAsARole() { + public void mod09test03OrganizationActingAsARole() { Assert.assertFalse(basicPage.orgStructure() .selectTabWithRootOrg("ExAmPLE, Inc. - Functional Structure") .getOrgHierarchyPanel() diff --git a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/page/TaskPageTest.java b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/page/TaskPageTest.java index 9d254111aaf..338d997a886 100644 --- a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/page/TaskPageTest.java +++ b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/page/TaskPageTest.java @@ -42,7 +42,9 @@ public void test001createNewTask() { .selectOption("objectType","User") .and() .and() - .clickSave(); + .clickSave() + .feedback() + .isSuccess(); ListTasksPage tasksPage = basicPage.listTasks(); PrismForm> taskForm = tasksPage diff --git a/testing/schrodingertest/src/test/resources/labs/objects/lookupTables/lookup-emp-status.xml b/testing/schrodingertest/src/test/resources/labs/objects/lookupTables/lookup-emp-status.xml new file mode 100644 index 00000000000..371b1b97762 --- /dev/null +++ b/testing/schrodingertest/src/test/resources/labs/objects/lookupTables/lookup-emp-status.xml @@ -0,0 +1,27 @@ + + + + Employee Status + + A + + + + I + + + + F + + + diff --git a/testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user-10-3.xml b/testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user-10-3.xml new file mode 100644 index 00000000000..a754ddf5f74 --- /dev/null +++ b/testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user-10-3.xml @@ -0,0 +1,247 @@ + + + + ExAmPLE User Template + + ExAmPLE, Inc. User Template: Fullname + Generate fullname (enforcing, no weak mapping) + strong + + givenName + + + familyName + + + + + + fullName + + + + ExAmPLE, Inc. User Template: Active org. assignment + strong + + extension/empStatus + + + + c:OrgType + + + c:name + ACTIVE + + + + + + assignment + + + + + + + ExAmPLE, Inc. User Template: Inactive org. assignment + strong + + extension/empStatus + + + + c:OrgType + + + c:name + INACTIVE + + + + + + assignment + + + + + + + ExAmPLE, Inc. User Template: Former employees org. assignment + strong + + extension/empStatus + + + + c:OrgType + + + c:name + FORMER + + + + + + assignment + + + + + + + ExAmPLE, Inc. User Template: Department org. assignment + strong + + extension/empStatus + + + + costCenter + + + + c:OrgType + + + + + c:name + + + + + + + + + + assignment + + + + + + + ExAmPLE, Inc. User Template: Manager of Department org. assignment + strong + + extension/isManager + + + extension/empStatus + + + costCenter + + + + + c:OrgType + + + + + c:name + + + + + + + + org:manager + + + + + assignment + + + + + + + Internal Employee role assignment + Assign 'Internal Employee' role to active and inactive users. + strong + + extension/empStatus + + + + c:RoleType + + + c:name + Internal Employee + + + + + + assignment + + + + + + + + extension/empStatus + + + + + telephoneNumber + Mobile Telephone Number + + + + honorificSuffix + + presentation + + true + false + false + + + + + diff --git a/testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user-simple.xml b/testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user-simple.xml new file mode 100644 index 00000000000..eaf643ec5ea --- /dev/null +++ b/testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user-simple.xml @@ -0,0 +1,36 @@ + + + + ExAmPLE User Template + + ExAmPLE, Inc. User Template: Fullname + Generate fullname (enforcing, no weak mapping) + strong + + givenName + + + familyName + + + + + + fullName + + + diff --git a/testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user.xml b/testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user.xml new file mode 100644 index 00000000000..e06f1920f72 --- /dev/null +++ b/testing/schrodingertest/src/test/resources/labs/objects/objectTemplate/object-template-example-user.xml @@ -0,0 +1,249 @@ + + + + ExAmPLE User Template + + ExAmPLE, Inc. User Template: Fullname + Generate fullname (enforcing, no weak mapping) + strong + + givenName + + + familyName + + + + + + fullName + + + + ExAmPLE, Inc. User Template: Active org. assignment + strong + + extension/empStatus + + + + c:OrgType + + + c:name + ACTIVE + + + + + + assignment + + + + + + + ExAmPLE, Inc. User Template: Inactive org. assignment + strong + + extension/empStatus + + + + c:OrgType + + + c:name + INACTIVE + + + + + + assignment + + + + + + + ExAmPLE, Inc. User Template: Former employees org. assignment + strong + + extension/empStatus + + + + c:OrgType + + + c:name + FORMER + + + + + + assignment + + + + + + + ExAmPLE, Inc. User Template: Department org. assignment + strong + + extension/empStatus + + + + costCenter + + + + c:OrgType + + + + + c:name + + + + + + + + + + assignment + + + + + + + ExAmPLE, Inc. User Template: Manager of Department org. assignment + strong + + extension/isManager + + + extension/empStatus + + + costCenter + + + + + c:OrgType + + + + + c:name + + + + + + + + org:manager + + + + + assignment + + + + + + + Internal Employee role assignment + Assign 'Internal Employee' role to active and inactive users. + strong + + extension/empStatus + + + + c:RoleType + + + c:name + Internal Employee + + + + + + assignment + + + + + + + + + diff --git a/testing/schrodingertest/src/test/resources/labs/objects/resources/localhost-csvfile-3-ldap-10-4.xml b/testing/schrodingertest/src/test/resources/labs/objects/resources/localhost-csvfile-3-ldap-10-4.xml new file mode 100644 index 00000000000..358910909c2 --- /dev/null +++ b/testing/schrodingertest/src/test/resources/labs/objects/resources/localhost-csvfile-3-ldap-10-4.xml @@ -0,0 +1,537 @@ + + + + + CSV-3 (LDAP) + + + + c:connectorType + com.evolveum.polygon.connector.csv.CsvConnector + + + + + + /opt/training/midpoint-labs/flatfiles/csv-3.csv + utf-8 + ALL + " + , + ; + dn + userPassword + + + + + account + default + Default Account + true + ri:AccountObjectClass + + ri:dn + Distinguished Name + + 0 + + + strong + + extension/ouPath + + + givenName + + + familyName + + + + + + + + ri:employeeNumber + Employee Number + Definition of Employee Number attribute handling. + + + employeeNumber + + + + + ri:givenName + First name + Definition of Firstname attribute handling. + + + givenName + + + + + ri:sn + Last name + Definition of Lastname attribute handling. + + + familyName + + + + + ri:mail + Mail + Definition of Mail attribute handling. + + + givenName + + + familyName + + + + + + + + emailAddress + + + + + ri:MemberOf + Member Of + Definition of MemberOf attribute handling. + + 0 + unbounded + + + + ri:description + Description + + + description + + + + + ri:manager + Manager + false + + strong + + + + + + + ri:telephoneNumber + Telephone Number + Phone number normalized spaces + + + telephoneNumber + + + + + + + + + + + + + http://prism.evolveum.com/xml/ns/public/matching-rule-3#stringIgnoreCase + attributes/ri:dn + cn=administrator,ou=ExAmPLE,dc=example,dc=com + + + + + + + http://prism.evolveum.com/xml/ns/public/matching-rule-3#stringIgnoreCase + attributes/ri:dn + cn=mail-daemon,ou=ExAmPLE,dc=example,dc=com + + + + + + + weak + + $focusExists + + + + + + + + + + + + + + + + + + + + account + admin + + Admin Account + false + + ri:AccountObjectClass + + ri:dn + Distinguished Name + + + 0 + + + + givenName + + + familyName + + + + + + + + + ri:employeeNumber + Employee Number + Definition of Employee Number attribute handling. + + + employeeNumber + + + + + ri:givenName + First name + Definition of Firstname attribute handling. + + + givenName + + + + + + ri:sn + Last name + Definition of Lastname attribute handling. + + + familyName + + + + + ri:MemberOf + Member Of + Definition of MemberOf attribute handling. + + 0 + unbounded + + + + + 10 + + + + + + + + + + http://prism.evolveum.com/xml/ns/public/matching-rule-3#stringIgnoreCase + attributes/ri:dn + cn=admin2,ou=_Administrators_,ou=ExAmPLE,dc=example,dc=com + + + + + + + http://prism.evolveum.com/xml/ns/public/matching-rule-3#stringIgnoreCase + attributes/ri:dn + cn=admin3,ou=_Administrators_,ou=ExAmPLE,dc=example,dc=com + + + + + + + + + + + + + + + + + + + + + + + + + ri:disabled + false + true + + + + sequentialSearch + + + + + + Default account + Normal accounts are NOT in ou=_Administrators container + account + default + true + + + + + + Correlation expression is a search query. + Following search queury will look for users that have "employeeNumber" + equal to the "employeeNumber" attribute of the account. + The condition will ensure that "employeeNumber" is not + empty, otherwise it would match any midPoint user + with empty "employeeNumber" attribute, such as "administrator". + The correlation rule always looks for users, so it will not match + any other object type. + + + c:employeeNumber + + $projection/attributes/ri:employeeNumber + + + + + + + + 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 + + + + Admin account + Admin accounts are in ou=_Administrators container + account + admin + true + + + + + + Correlation expression is a search query. + Following search queury will look for users that have "employeeNumber" + equal to the "employeeNumber" attribute of the account. + The condition will ensure that "employeeNumber" is not + empty, otherwise it would match any midPoint user + with empty "employeeNumber" attribute, such as "administrator". + The correlation rule always looks for users, so it will not match + any other object type. + + + c:employeeNumber + + $projection/attributes/ri:employeeNumber + + + + + + + + + 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 + + + + + diff --git a/testing/schrodingertest/src/test/resources/labs/objects/resources/localhost-csvfile-3-ldap-4-4.xml b/testing/schrodingertest/src/test/resources/labs/objects/resources/localhost-csvfile-3-ldap-4-4.xml index 3a88b65c00c..4e95918757f 100644 --- a/testing/schrodingertest/src/test/resources/labs/objects/resources/localhost-csvfile-3-ldap-4-4.xml +++ b/testing/schrodingertest/src/test/resources/labs/objects/resources/localhost-csvfile-3-ldap-4-4.xml @@ -164,7 +164,6 @@ ri:manager Manager - false strong diff --git a/testing/schrodingertest/src/test/resources/labs/sources/source-10-1.csv b/testing/schrodingertest/src/test/resources/labs/sources/source-10-1.csv new file mode 100644 index 00000000000..049564c703b --- /dev/null +++ b/testing/schrodingertest/src/test/resources/labs/sources/source-10-1.csv @@ -0,0 +1,17 @@ +"name","firstName","lastName","position","ouNumber","department","ouPath","employedFrom","employedTo","isManager","empStatus" +"001212","John","Smith","Human Resources Specialist","0300","Human Resources","0300","2000-05-01T08:00:00",,1,"A" +"000090","Mel","Austenberg","General Manager","0100","Executive Division","0100","2008-06-01T08:00:00",,1,"A" +"001049","Ellen","Feckerwood","Assistant","0100","Executive Division","0100","2001-02-01T09:01:00",,0,"I" +"000045","Andreas","Decker","Sales Manager","0110","Sales Department","0100:0110","2010-07-15T08:20:00",,0,"A" +"000021","Rudie","Mechal","Assistant","0100","Executive Division","0100","2010-07-15T08:20:00",,0,"A" +"000089","Ivan","Rockerteller","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000005","Eve","Morthanic","Executive Manager","0100","Executive Division","0100","2010-07-15T08:20:00",,1,"A" +"000035","James","Bradley","Technology Division Manager","0200","Technology Division","0200","2010-07-15T08:20:00",,1,"A" +"000078","Kyle","Harrison","Analyst","0210","Software Department","0200:0210","2010-07-15T08:20:00",,0,"A" +"000002","Richard","Dwayne","General Manager",,"Executive Division","0100","2000-05-01T08:00:00","2012-09-30T17:35:00",0,"F" +"000389","Ann","De Wries","Sales Manager","0110","Sales Department","0100:0110","2010-07-15T08:20:00",,0,"A" +"000158","Alice","Black","IT Administrator","0212","IT Administration Department","0200:0212","2010-07-15T08:20:00",,0,"A" +"000390","John","Wicks","IT Manager","0212","IT Administration Department","0200:0212","2010-07-15T08:20:00",,1,"A" +"000328","Adele","Dewrieux","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000999","Arnold J.","Rimmer","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000998","David","Lister","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" diff --git a/testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part1.csv b/testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part1.csv new file mode 100644 index 00000000000..7e9963f2b12 --- /dev/null +++ b/testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part1.csv @@ -0,0 +1,17 @@ +"name","firstName","lastName","position","ouNumber","department","ouPath","employedFrom","employedTo","isManager","empStatus" +"001212","John","Smith","Human Resources Specialist","0300","Human Resources","0300","2000-05-01T08:00:00",,1,"A" +"000090","Mel","Austenberg","General Manager","0100","Executive Division","0100","2008-06-01T08:00:00",,1,"A" +"001049","Ellen","Feckerwood","Assistant","0100","Executive Division","0100","2001-02-01T09:01:00",,0,"I" +"000045","Andreas","Decker","Sales Manager","0110","Sales Department","0100:0110","2010-07-15T08:20:00",,0,"A" +"000021","Rudie","Mechal","Assistant","0100","Executive Division","0100","2010-07-15T08:20:00",,0,"A" +"000089","Ivan","Rockerteller","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000005","Eve","Morthanic","Executive Manager","0100","Executive Division","0100","2010-07-15T08:20:00",,1,"A" +"000035","James","Bradley","Technology Division Manager","0200","Technology Division","0200","2010-07-15T08:20:00",,1,"A" +"000078","Kyle","Harrison","Analyst","0210","Software Department","0200:0210","2010-07-15T08:20:00",,0,"A" +"000002","Richard","Dwayne","General Manager",,"Executive Division","0100","2000-05-01T08:00:00","2012-09-30T17:35:00",0,"F" +"000389","Ann","De Wries","Sales Manager","0110","Sales Department","0100:0110","2010-07-15T08:20:00",,0,"A" +"000158","Alice","Black","IT Administrator","0212","IT Administration Department","0200:0212","2010-07-15T08:20:00",,0,"A" +"000390","John","Wicks","IT Manager","0212","IT Administration Department","0200:0212","2010-07-15T08:20:00",,1,"A" +"000328","Adele","Dewrieux","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000999","Arnold Judas","Rimmer","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000998","David","Lister","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" diff --git a/testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part2.csv b/testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part2.csv new file mode 100644 index 00000000000..ea99890f893 --- /dev/null +++ b/testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part2.csv @@ -0,0 +1,17 @@ +"name","firstName","lastName","position","ouNumber","department","ouPath","employedFrom","employedTo","isManager","empStatus" +"001212","John","Smith","Human Resources Specialist","0300","Human Resources","0300","2000-05-01T08:00:00",,1,"A" +"000090","Mel","Austenberg","General Manager","0100","Executive Division","0100","2008-06-01T08:00:00",,1,"A" +"001049","Ellen","Feckerwood","Assistant","0100","Executive Division","0100","2001-02-01T09:01:00",,0,"I" +"000045","Andreas","Decker","Sales Manager","0110","Sales Department","0100:0110","2010-07-15T08:20:00",,0,"A" +"000021","Rudie","Mechal","Assistant","0100","Executive Division","0100","2010-07-15T08:20:00",,0,"A" +"000089","Ivan","Rockerteller","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000005","Eve","Morthanic","Executive Manager","0100","Executive Division","0100","2010-07-15T08:20:00",,1,"A" +"000035","James","Bradley","Technology Division Manager","0200","Technology Division","0200","2010-07-15T08:20:00",,1,"A" +"000078","Kyle","Harrison","Analyst","0210","Software Department","0200:0210","2010-07-15T08:20:00",,0,"A" +"000002","Richard","Dwayne","General Manager",,"Executive Division","0100","2000-05-01T08:00:00","2012-09-30T17:35:00",0,"F" +"000389","Ann","De Wries","Sales Manager","0110","Sales Department","0100:0110","2010-07-15T08:20:00",,0,"A" +"000158","Alice","Black","IT Administrator","0212","IT Administration Department","0200:0212","2010-07-15T08:20:00",,0,"A" +"000390","John","Wicks","IT Manager","0212","IT Administration Department","0200:0212","2010-07-15T08:20:00",,1,"A" +"000328","Adele","Dewrieux","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000999","Arnold Judas","Rimmer","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000998","David","Lister","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"I" diff --git a/testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part3.csv b/testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part3.csv new file mode 100644 index 00000000000..a1b8e8b83ca --- /dev/null +++ b/testing/schrodingertest/src/test/resources/labs/sources/source-10-2-part3.csv @@ -0,0 +1,17 @@ +"name","firstName","lastName","position","ouNumber","department","ouPath","employedFrom","employedTo","isManager","empStatus" +"001212","John","Smith","Human Resources Specialist","0300","Human Resources","0300","2000-05-01T08:00:00",,1,"A" +"000090","Mel","Austenberg","General Manager","0100","Executive Division","0100","2008-06-01T08:00:00",,1,"A" +"001049","Ellen","Feckerwood","Assistant","0100","Executive Division","0100","2001-02-01T09:01:00",,0,"I" +"000045","Andreas","Decker","Sales Manager","0110","Sales Department","0100:0110","2010-07-15T08:20:00",,0,"A" +"000021","Rudie","Mechal","Assistant","0100","Executive Division","0100","2010-07-15T08:20:00",,0,"A" +"000089","Ivan","Rockerteller","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000005","Eve","Morthanic","Executive Manager","0100","Executive Division","0100","2010-07-15T08:20:00",,1,"A" +"000035","James","Bradley","Technology Division Manager","0200","Technology Division","0200","2010-07-15T08:20:00",,1,"A" +"000078","Kyle","Harrison","Analyst","0210","Software Department","0200:0210","2010-07-15T08:20:00",,0,"A" +"000002","Richard","Dwayne","General Manager",,"Executive Division","0100","2000-05-01T08:00:00","2012-09-30T17:35:00",0,"F" +"000389","Ann","De Wries","Sales Manager","0110","Sales Department","0100:0110","2010-07-15T08:20:00",,0,"A" +"000158","Alice","Black","IT Administrator","0212","IT Administration Department","0200:0212","2010-07-15T08:20:00",,0,"A" +"000390","John","Wicks","IT Manager","0212","IT Administration Department","0200:0212","2010-07-15T08:20:00",,1,"A" +"000328","Adele","Dewrieux","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000999","Arnold Judas","Rimmer","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"A" +"000998","David","Lister","Application Developer","0211","Java Development","0200:0210:0211","2010-07-15T08:20:00",,0,"F" diff --git a/testing/schrodingertest/testng-integration.xml b/testing/schrodingertest/testng-integration.xml index 059412d8252..8d074766a6d 100644 --- a/testing/schrodingertest/testng-integration.xml +++ b/testing/schrodingertest/testng-integration.xml @@ -117,6 +117,7 @@ +
\ No newline at end of file diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/AssignmentsTab.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/AssignmentsTab.java index 039008e1c51..e0888afbbfa 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/AssignmentsTab.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/AssignmentsTab.java @@ -8,6 +8,7 @@ package com.evolveum.midpoint.schrodinger.component; import com.codeborne.selenide.Condition; +import com.codeborne.selenide.ElementsCollection; import com.codeborne.selenide.Selenide; import com.codeborne.selenide.SelenideElement; import com.evolveum.midpoint.schrodinger.MidPoint; @@ -18,6 +19,10 @@ import com.evolveum.midpoint.schrodinger.page.AssignmentHolderDetailsPage; import com.evolveum.midpoint.schrodinger.util.Schrodinger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import static com.codeborne.selenide.Selenide.$; /** @@ -134,4 +139,18 @@ protected void selectType(String resourceKey) { $(Schrodinger.byDataId("div", resourceKey)).click(); Selenide.sleep(MidPoint.TIMEOUT_DEFAULT_2_S); } + + public boolean containsAssignmentsWithRelation(String relation, String... expectedAssignments) { + String relationString = relation.equals("Default") ? "" : ("Relation: " + relation); + ElementsCollection labels = getParentElement() + .$$(Schrodinger.byAncestorFollowingSiblingDescendantOrSelfElementEnclosedValue("span", "data-s-id", "label", + "data-s-id", "5", relationString)); + List indirectAssignments = new ArrayList(); + for (SelenideElement label : labels) { + if (!label.getText().isEmpty()) { + indirectAssignments.add(label.getText()); + } + } + return indirectAssignments.containsAll(Arrays.asList(expectedAssignments)); + } } diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/PrismForm.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/PrismForm.java index 0cf6d26439c..f82bbe42600 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/PrismForm.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/PrismForm.java @@ -120,6 +120,12 @@ public Boolean compareInputAttributeValue(String name, String expectedValue) { } + public Boolean isPropertyEnabled(String name) { + SelenideElement property = findProperty(name); + SelenideElement valueElement = property.parent().$(org.openqa.selenium.By.xpath(".//*[contains(@class,\"form-control\")]")); + return valueElement.isEnabled(); + } + public Boolean compareInputAttributeValues(String name, String... expectedValues) { return compareInputAttributeValues(name, Arrays.asList(expectedValues)); } diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/PrismFormWithActionButtons.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/PrismFormWithActionButtons.java index 59b181af35f..573d03f81bd 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/PrismFormWithActionButtons.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/PrismFormWithActionButtons.java @@ -23,7 +23,7 @@ public PrismFormWithActionButtons(T parent, SelenideElement parentElement) { public T clickDone() { - $(Schrodinger.byDataResourceKey("div", "AssignmentPanel.doneButton")) + $(Schrodinger.byDataResourceKey("div", "MultivalueContainerListPanel.doneButton")) .waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); return this.getParent(); @@ -31,7 +31,7 @@ public T clickDone() { public T clickCancel() { - $(Schrodinger.byDataResourceKey("div", "AssignmentPanel.cancelButton")) + $(Schrodinger.byDataResourceKey("div", "MultivalueContainerListPanel.cancelButton")) .waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); return this.getParent(); diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/configuration/ObjectPolicyTab.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/configuration/ObjectPolicyTab.java new file mode 100644 index 00000000000..29483722502 --- /dev/null +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/configuration/ObjectPolicyTab.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010-2019 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.schrodinger.component.configuration; + +import com.codeborne.selenide.Condition; +import com.codeborne.selenide.SelenideElement; + +import com.evolveum.midpoint.schrodinger.MidPoint; +import com.evolveum.midpoint.schrodinger.component.Component; +import com.evolveum.midpoint.schrodinger.component.ProjectionsTab; +import com.evolveum.midpoint.schrodinger.component.common.PrismFormWithActionButtons; +import com.evolveum.midpoint.schrodinger.component.common.table.AbstractTableWithPrismView; +import com.evolveum.midpoint.schrodinger.component.modal.FocusSetProjectionModal; +import com.evolveum.midpoint.schrodinger.component.user.ProjectionsDropDown; +import com.evolveum.midpoint.schrodinger.page.configuration.SystemPage; +import com.evolveum.midpoint.schrodinger.util.Schrodinger; + +import org.openqa.selenium.By; + +import static com.codeborne.selenide.Selenide.$; + +/** + * @author skublik + */ + +public class ObjectPolicyTab extends Component { + + public ObjectPolicyTab(SystemPage parent, SelenideElement parentElement) { + super(parent, parentElement); + } + + public AbstractTableWithPrismView table() { + + SelenideElement tableBox = $(Schrodinger.byDataId("div", "itemsTable")); + + return new AbstractTableWithPrismView(this, tableBox) { + @Override + public PrismFormWithActionButtons> clickByName(String name) { + + $(Schrodinger.byElementValue("span", "data-s-id", "label", name)) + .waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); + + SelenideElement prismElement = $(Schrodinger.byDataId("div", "itemDetails")) + .waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S); + + return new PrismFormWithActionButtons<>(this, prismElement); + } + + @Override + public AbstractTableWithPrismView selectCheckboxByName(String name) { + + $(Schrodinger.byFollowingSiblingEnclosedValue("td", "class", "check", "data-s-id", "2", name)) + .$(By.tagName("input")) + .waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); + + return this; + } + + @Override + public AbstractTableWithPrismView removeByName(String name) { + //TODO implement + return this; + } + }; + } + + public PrismFormWithActionButtons clickAddObjectPolicy() { + SelenideElement plusButton = $(Schrodinger.byElementAttributeValue("i", "class", "fa fa-plus ")); + plusButton.waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); + plusButton.waitWhile(Condition.visible, MidPoint.TIMEOUT_LONG_1_M); + SelenideElement prismElement = $(Schrodinger.byDataId("div", "itemDetails")) + .waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S); + + return new PrismFormWithActionButtons(this, prismElement); + } + +} diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/configuration/SystemTab.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/configuration/SystemTab.java index 145072d45e4..f4a2b24ff77 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/configuration/SystemTab.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/configuration/SystemTab.java @@ -7,7 +7,10 @@ package com.evolveum.midpoint.schrodinger.component.configuration; import com.codeborne.selenide.SelenideElement; + +import com.evolveum.midpoint.schrodinger.component.AssignmentHolderBasicTab; import com.evolveum.midpoint.schrodinger.component.Component; +import com.evolveum.midpoint.schrodinger.component.common.PrismForm; import com.evolveum.midpoint.schrodinger.page.configuration.SystemPage; /** @@ -19,6 +22,11 @@ public SystemTab(SystemPage parent, SelenideElement parentElement) { super(parent, parentElement); } + public PrismForm form() { + SelenideElement element = null; + return new PrismForm(this, element); + } + public void auditRecordsCleanupInterval(String interval) { // todo implement } diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/org/ManagerPanel.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/org/ManagerPanel.java new file mode 100644 index 00000000000..d7e3887392f --- /dev/null +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/org/ManagerPanel.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010-2019 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.schrodinger.component.org; + +import com.codeborne.selenide.ElementsCollection; +import com.codeborne.selenide.SelenideElement; + +import com.evolveum.midpoint.schrodinger.component.Component; +import com.evolveum.midpoint.schrodinger.util.Schrodinger; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author skublik + */ + +public class ManagerPanel extends Component { + + public ManagerPanel(T parent, SelenideElement parentElement) { + super(parent, parentElement); + } + + public boolean containsManager(String... expectedManagers) { + ElementsCollection managersElements = getParentElement().$$x(".//span[@" + Schrodinger.DATA_S_ID + "='summaryDisplayName']"); + List managers = new ArrayList(); + for (SelenideElement managerElement : managersElements) { + managers.add(managerElement.getText()); + } + + return managers.containsAll(Arrays.asList(expectedManagers)); + } +} diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/org/OrgRootTab.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/org/OrgRootTab.java index aba7abff506..0a73f152e01 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/org/OrgRootTab.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/org/OrgRootTab.java @@ -36,4 +36,9 @@ public MemberPanel getMemberPanel() { SelenideElement memberPanel = getParentElement().$(Schrodinger.byDataId("div", "memberPanel")); return new MemberPanel<>(this, memberPanel); } + + public ManagerPanel getManagerPanel() { + SelenideElement memberPanel = getParentElement().$(Schrodinger.byDataId("div", "managerContainer")); + return new ManagerPanel<>(this, memberPanel); + } } diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/table/TableHeaderDropDownMenu.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/table/TableHeaderDropDownMenu.java index 19f0fdf3887..1fd6da7acb8 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/table/TableHeaderDropDownMenu.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/table/TableHeaderDropDownMenu.java @@ -6,9 +6,14 @@ */ package com.evolveum.midpoint.schrodinger.component.table; +import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; +import com.evolveum.midpoint.schrodinger.MidPoint; import com.evolveum.midpoint.schrodinger.component.common.DropDown; +import com.evolveum.midpoint.schrodinger.util.Schrodinger; + +import static com.codeborne.selenide.Selenide.$; /** * @author skublik @@ -20,4 +25,11 @@ public TableHeaderDropDownMenu(T parent, SelenideElement parentElement) { super(parent, parentElement); } + protected T clickByDataResourceKey(String key) { + $(Schrodinger.byDataResourceKey(key)) + .waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); + + return this.getParent(); + } + } diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/user/ProjectionsDropDown.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/user/ProjectionsDropDown.java index 3d348c088fa..a9f5f788804 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/user/ProjectionsDropDown.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/user/ProjectionsDropDown.java @@ -28,31 +28,19 @@ public ProjectionsDropDown(T parent, SelenideElement parentElement) { } public T enable() { - $(Schrodinger.byDataResourceKey("pageAdminFocus.button.enable")) - .waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); - - return this.getParent(); + return clickByDataResourceKey("pageAdminFocus.button.enable"); } public T disable() { - $(Schrodinger.byDataResourceKey("pageAdminFocus.button.disable")) - .waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); - - return this.getParent(); + return clickByDataResourceKey("pageAdminFocus.button.disable"); } public T unlink() { - $(Schrodinger.byDataResourceKey("pageAdminFocus.button.unlink")) - .waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); - - return this.getParent(); + return clickByDataResourceKey("pageAdminFocus.button.unlink"); } public T unlock() { - $(Schrodinger.byDataResourceKey("pageAdminFocus.button.unlock")) - .waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); - - return this.getParent(); + return clickByDataResourceKey("pageAdminFocus.button.unlock"); } // public FocusSetProjectionModal addProjection() { diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/page/BasicPage.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/page/BasicPage.java index 2200b723a9e..fc3aec85bbe 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/page/BasicPage.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/page/BasicPage.java @@ -290,6 +290,11 @@ public SystemTab system() { return new SystemPage().systemTab(); } + public ObjectPolicyTab objectPolicy() { + clickConfigurationMenu("PageAdmin.menu.top.configuration.basic", "PageAdmin.menu.top.configuration.objectPolicy"); + return new SystemPage().objectPolicyTab(); + } + public NotificationsTab notifications() { clickConfigurationMenu("PageAdmin.menu.top.configuration.basic", "PageAdmin.menu.top.configuration.notifications"); return new SystemPage().notificationsTab(); diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/page/configuration/SystemPage.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/page/configuration/SystemPage.java index 6d4e9898f9d..020cb382444 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/page/configuration/SystemPage.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/page/configuration/SystemPage.java @@ -32,13 +32,17 @@ public SystemPage save() { } public SystemTab systemTab() { - //todo implement - SelenideElement element = null; + SelenideElement element = getTabPanel().clickTab("pageSystemConfiguration.system.title"); return new SystemTab(this, element); } + public ObjectPolicyTab objectPolicyTab() { + SelenideElement element = getTabPanel().clickTab("pageSystemConfiguration.objectPolicy.title"); + return new ObjectPolicyTab(this, element); + } + public NotificationsTab notificationsTab() { - SelenideElement element = findTabPanel().clickTab("pageSystemConfiguration.notifications.title"); + SelenideElement element = getTabPanel().clickTab("pageSystemConfiguration.notifications.title"); return new NotificationsTab(this, element); } @@ -61,7 +65,7 @@ public AdminGuiTab adminGuiTab() { } public InfrastructureTab infrastructureTab() { - SelenideElement element = findTabPanel().clickTab("pageSystemConfiguration.infrastructure.title"); + SelenideElement element = getTabPanel().clickTab("pageSystemConfiguration.infrastructure.title"); return new InfrastructureTab(this, element); } @@ -69,7 +73,7 @@ public RoleManagementTab roleManagementTab(){ return new RoleManagementTab(this, null); } - protected TabPanel findTabPanel() { + protected TabPanel getTabPanel() { SelenideElement tabPanelElement = $(Schrodinger.byDataId("div", "tabPanel")) .waitUntil(Condition.appear, MidPoint.TIMEOUT_DEFAULT_2_S); return new TabPanel<>(this, tabPanelElement); From fb3d4576ffd8caa3867101b99ab2cc9758b3ecfe Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Mon, 27 Apr 2020 19:10:13 +0200 Subject: [PATCH 4/6] schema/POM: trying to suppress remote schema checking for wsdl2java --- infra/schema/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/infra/schema/pom.xml b/infra/schema/pom.xml index 5a04f9a9ceb..61392356f26 100644 --- a/infra/schema/pom.xml +++ b/infra/schema/pom.xml @@ -273,6 +273,7 @@ + -Djavax.xml.accessExternalSchema=file wsdl2java From 4870c5a615a49a2ffb223e6f4d5d6f735cb197df Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Mon, 27 Apr 2020 19:35:42 +0200 Subject: [PATCH 5/6] schema/POM: different take on suppressing remote schema checking --- infra/schema/pom.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/infra/schema/pom.xml b/infra/schema/pom.xml index 61392356f26..101e1aa7e90 100644 --- a/infra/schema/pom.xml +++ b/infra/schema/pom.xml @@ -220,7 +220,6 @@ -impl -verbose - -xjc-Xts -xjc-XhashCode @@ -236,7 +235,6 @@ http://prism.evolveum.com/xml/ns/public/annotation-3 -nexclude http://prism.evolveum.com/xml/ns/public/query-3 - -xjc-Xmidpoint @@ -252,7 +250,6 @@ -impl -verbose - -xjc-Xts -xjc-XhashCode @@ -268,12 +265,11 @@ http://prism.evolveum.com/xml/ns/public/annotation-3 -nexclude http://prism.evolveum.com/xml/ns/public/query-3 - -xjc-Xmidpoint - -Djavax.xml.accessExternalSchema=file + -Dcom.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.noCorrectnessCheck=true wsdl2java @@ -351,4 +347,3 @@ - From a1317923ebd32935607260130ab9782786efbf4c Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Mon, 27 Apr 2020 21:56:07 +0200 Subject: [PATCH 6/6] schema/POM: comment for -D...SchemaCompilerImpl.noCorrectnessCheck=true --- infra/schema/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/infra/schema/pom.xml b/infra/schema/pom.xml index 101e1aa7e90..aff532b9c29 100644 --- a/infra/schema/pom.xml +++ b/infra/schema/pom.xml @@ -269,6 +269,10 @@ + -Dcom.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.noCorrectnessCheck=true