diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/autocomplete/AutoCompleteTextPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/autocomplete/AutoCompleteTextPanel.java index 33131f145c7..a715ecc0852 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/autocomplete/AutoCompleteTextPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/autocomplete/AutoCompleteTextPanel.java @@ -1,207 +1,164 @@ -/* - * Copyright (c) 2010-2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ - -package com.evolveum.midpoint.gui.api.component.autocomplete; - -import java.util.Iterator; -import java.util.Locale; - -import org.apache.wicket.ajax.AjaxRequestTarget; -import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; -import org.apache.wicket.ajax.attributes.ThrottlingSettings; -import org.apache.wicket.ajax.form.OnChangeAjaxBehavior; -import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteSettings; -import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteTextField; -import org.apache.wicket.extensions.ajax.markup.html.autocomplete.IAutoCompleteRenderer; -import org.apache.wicket.extensions.ajax.markup.html.autocomplete.StringAutoCompleteRenderer; -import org.apache.wicket.markup.html.form.FormComponent; -import org.apache.wicket.model.IModel; -import org.apache.wicket.util.convert.ConversionException; -import org.apache.wicket.util.convert.IConverter; -import org.apache.wicket.util.time.Duration; - -import com.evolveum.midpoint.gui.api.util.WebComponentUtil; -import com.evolveum.midpoint.web.model.LookupPropertyModel; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; - -/** - * Autocomplete field for Strings. - * - * TODO: may need some work to properly support non-string values. - * - * @author shood - * @author semancik - * */ -public abstract class AutoCompleteTextPanel extends AbstractAutoCompletePanel { - private static final long serialVersionUID = 1L; - - private static final String ID_INPUT = "input"; - - private LookupTableType lookupTable = null; - private boolean strict; - -// public AutoCompleteTextPanel(String id, final IModel model, Class type) { -// this(id, model, type, StringAutoCompleteRenderer.INSTANCE); -// } - - public AutoCompleteTextPanel(String id, final IModel model, Class type, boolean strict, LookupTableType lookuptable) { - this(id, model, type, StringAutoCompleteRenderer.INSTANCE); - this.lookupTable = lookuptable; - this.strict = strict; - } - - public AutoCompleteTextPanel(String id, final IModel model, Class type, IAutoCompleteRenderer renderer) { - super(id); - - AutoCompleteSettings autoCompleteSettings = createAutoCompleteSettings(); - - // this has to be copied because the AutoCompleteTextField dies if renderer=null - final AutoCompleteTextField input = new AutoCompleteTextField(ID_INPUT, model, type, renderer, autoCompleteSettings) { - private static final long serialVersionUID = 1L; - - @Override - protected Iterator getChoices(String input) { - return getIterator(input); - } - - @Override - protected void updateAjaxAttributes(AjaxRequestAttributes attributes){ - super.updateAjaxAttributes(attributes); - attributes.setThrottlingSettings(new ThrottlingSettings(Duration.ONE_SECOND, true)); //TODO move to the autocompelete settings - } - - @Override - public IConverter getConverter(Class type) { - IConverter converter = super.getConverter(type); - if (lookupTable == null) { - return converter; - } - - return new LookupTableConverter<>(converter); - - } - }; - - input.setType(type); - if (model instanceof LookupPropertyModel) { - input.add(new OnChangeAjaxBehavior() { - private static final long serialVersionUID = 1L; - - @Override - protected void onUpdate(AjaxRequestTarget target) { - checkInputValue(input, target, (LookupPropertyModel)model); - } - - @Override - protected void updateAjaxAttributes(AjaxRequestAttributes attributes){ - super.updateAjaxAttributes(attributes); - attributes.setThrottlingSettings(new ThrottlingSettings(Duration.ONE_SECOND, true)); - } - }); - } - add(input); - } - - /** - * This method takes care of retrieving an iterator over all - * options that can be completed. The generation of options can be - * affected by using current users input in 'input' variable. - * */ - public abstract Iterator getIterator(String input); - - @Override - public FormComponent getBaseFormComponent() { - return (FormComponent) get(ID_INPUT); - } - - //by default the method will check if AutoCompleteTextField input is empty - // and if yes, set empty value to model. This method is necessary because - // AutoCompleteTextField doesn't set value to model until it is unfocused - public void checkInputValue(AutoCompleteTextField input, AjaxRequestTarget target, LookupPropertyModel model){ - if (input.getInput() == null || input.getInput().trim().equals("")){ - model.setObject(input.getInput()); - } - if (!getIterator(input.getInput()).hasNext()) { - updateFeedbackPanel(input, true, target); - } else { - Iterator lookupTableValuesIterator = (Iterator) getIterator(input.getInput()); - - String value = input.getInput(); - boolean isValueExist = false; - String existingValue = ""; - if (value != null) { - if (value.trim().equals("")) { - isValueExist = true; - } else { - while (lookupTableValuesIterator.hasNext()) { - String lookupTableValue = lookupTableValuesIterator.next(); - if (value.trim().equalsIgnoreCase(lookupTableValue)) { - isValueExist = true; - existingValue = lookupTableValue; - break; - } - } - } - } - if (isValueExist) { - input.setModelValue(new String[]{existingValue}); - updateFeedbackPanel(input, false, target); - } else { - updateFeedbackPanel(input, true, target); - } - } - } - - class LookupTableConverter implements IConverter { - - private static final long serialVersionUID = 1L; - IConverter originConverter; - - public LookupTableConverter(IConverter originConverter) { - this.originConverter = originConverter; - } - - @Override - public C convertToObject(String value, Locale locale) throws ConversionException { - for (LookupTableRowType row : lookupTable.getRow()) { - if (value.equals(WebComponentUtil.getLocalizedOrOriginPolyStringValue(row.getLabel() != null ? row.getLabel().toPolyString() : null))) { - return originConverter.convertToObject(row.getKey(), locale); - } - } - boolean differentValue = true; - if (getBaseFormComponent() != null && getBaseFormComponent().getModelObject() != null - && getBaseFormComponent().getModelObject().equals(value)) { - differentValue = false; - } - - if (differentValue && strict) { - throw new ConversionException("Cannot convert " + value); - } - - return originConverter.convertToObject(value, locale); - - } - - @Override - public String convertToString(C key, Locale arg1) { - if (lookupTable != null) { - for (LookupTableRowType row : lookupTable.getRow()) { - if (key.toString().equals(row.getKey())) { - return WebComponentUtil.getLocalizedOrOriginPolyStringValue(row.getLabel() != null ? row.getLabel().toPolyString() : null); - } - } - } - return key.toString(); - } - } - - protected void updateFeedbackPanel(AutoCompleteTextField input, boolean isError, AjaxRequestTarget target){ - - } -} - +/* + * Copyright (c) 2010-2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.gui.api.component.autocomplete; + +import java.util.Iterator; +import java.util.Locale; + +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; +import org.apache.wicket.ajax.attributes.ThrottlingSettings; +import org.apache.wicket.ajax.form.OnChangeAjaxBehavior; +import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteSettings; +import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteTextField; +import org.apache.wicket.extensions.ajax.markup.html.autocomplete.IAutoCompleteRenderer; +import org.apache.wicket.extensions.ajax.markup.html.autocomplete.StringAutoCompleteRenderer; +import org.apache.wicket.markup.html.form.FormComponent; +import org.apache.wicket.model.IModel; +import org.apache.wicket.util.convert.ConversionException; +import org.apache.wicket.util.convert.IConverter; +import org.apache.wicket.util.time.Duration; + +import com.evolveum.midpoint.gui.api.util.WebComponentUtil; +import com.evolveum.midpoint.web.model.LookupPropertyModel; +import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; + +/** + * Autocomplete field for Strings. + * + * TODO: may need some work to properly support non-string values. + * + * @author shood + * @author semancik + * */ +public abstract class AutoCompleteTextPanel extends AbstractAutoCompletePanel { + private static final long serialVersionUID = 1L; + + private static final String ID_INPUT = "input"; + + private LookupTableType lookupTable = null; + private boolean strict; + +// public AutoCompleteTextPanel(String id, final IModel model, Class type) { +// this(id, model, type, StringAutoCompleteRenderer.INSTANCE); +// } + + public AutoCompleteTextPanel(String id, final IModel model, Class type, boolean strict, LookupTableType lookuptable) { + this(id, model, type, StringAutoCompleteRenderer.INSTANCE); + this.lookupTable = lookuptable; + this.strict = strict; + } + + public AutoCompleteTextPanel(String id, final IModel model, Class type, IAutoCompleteRenderer renderer) { + super(id); + + AutoCompleteSettings autoCompleteSettings = createAutoCompleteSettings(); + + // this has to be copied because the AutoCompleteTextField dies if renderer=null + final AutoCompleteTextField input = new AutoCompleteTextField(ID_INPUT, model, type, renderer, autoCompleteSettings) { + private static final long serialVersionUID = 1L; + + @Override + protected Iterator getChoices(String input) { + return getIterator(input); + } + + @Override + protected void updateAjaxAttributes(AjaxRequestAttributes attributes){ + super.updateAjaxAttributes(attributes); + attributes.setThrottlingSettings(new ThrottlingSettings(Duration.ONE_SECOND, true)); //TODO move to the autocompelete settings + } + + @Override + public IConverter getConverter(Class type) { + IConverter converter = super.getConverter(type); + if (lookupTable == null) { + return converter; + } + + return new LookupTableConverter<>(converter, lookupTable, getBaseFormComponent(), strict); + + } + }; + + input.setType(type); + if (model instanceof LookupPropertyModel) { + input.add(new OnChangeAjaxBehavior() { + private static final long serialVersionUID = 1L; + + @Override + protected void onUpdate(AjaxRequestTarget target) { + checkInputValue(input, target, (LookupPropertyModel)model); + } + + @Override + protected void updateAjaxAttributes(AjaxRequestAttributes attributes){ + super.updateAjaxAttributes(attributes); + attributes.setThrottlingSettings(new ThrottlingSettings(Duration.ONE_SECOND, true)); + } + }); + } + add(input); + } + + /** + * This method takes care of retrieving an iterator over all + * options that can be completed. The generation of options can be + * affected by using current users input in 'input' variable. + * */ + public abstract Iterator getIterator(String input); + + @Override + public FormComponent getBaseFormComponent() { + return (FormComponent) get(ID_INPUT); + } + + //by default the method will check if AutoCompleteTextField input is empty + // and if yes, set empty value to model. This method is necessary because + // AutoCompleteTextField doesn't set value to model until it is unfocused + public void checkInputValue(AutoCompleteTextField input, AjaxRequestTarget target, LookupPropertyModel model){ + if (input.getInput() == null || input.getInput().trim().equals("")){ + model.setObject(input.getInput()); + } + if (!getIterator(input.getInput()).hasNext()) { + updateFeedbackPanel(input, true, target); + } else { + Iterator lookupTableValuesIterator = (Iterator) getIterator(input.getInput()); + + String value = input.getInput(); + boolean isValueExist = false; + String existingValue = ""; + if (value != null) { + if (value.trim().equals("")) { + isValueExist = true; + } else { + while (lookupTableValuesIterator.hasNext()) { + String lookupTableValue = lookupTableValuesIterator.next(); + if (value.trim().equalsIgnoreCase(lookupTableValue)) { + isValueExist = true; + existingValue = lookupTableValue; + break; + } + } + } + } + if (isValueExist) { + input.setModelValue(new String[]{existingValue}); + updateFeedbackPanel(input, false, target); + } else { + updateFeedbackPanel(input, true, target); + } + } + } + + protected void updateFeedbackPanel(AutoCompleteTextField input, boolean isError, AjaxRequestTarget target){ + + } +} + diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/autocomplete/LookupTableConverter.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/autocomplete/LookupTableConverter.java new file mode 100644 index 00000000000..d9270a7ea01 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/autocomplete/LookupTableConverter.java @@ -0,0 +1,71 @@ +/* + * 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.gui.api.component.autocomplete; + +import com.evolveum.midpoint.gui.api.util.WebComponentUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; + +import org.apache.wicket.markup.html.form.FormComponent; +import org.apache.wicket.util.convert.ConversionException; +import org.apache.wicket.util.convert.IConverter; + +import java.util.Locale; + +/** + * @author skublik + */ + +public class LookupTableConverter implements IConverter { + + private static final long serialVersionUID = 1L; + private IConverter originConverter; + private LookupTableType lookupTable = null; + private FormComponent baseComponent; + private boolean strict; + + public LookupTableConverter(IConverter originConverter, LookupTableType lookupTable, FormComponent baseComponent, boolean strict) { + this.originConverter = originConverter; + this.lookupTable = lookupTable; + this.baseComponent = baseComponent; + this.strict = strict; + } + + @Override + public C convertToObject(String value, Locale locale) throws ConversionException { + for (LookupTableRowType row : lookupTable.getRow()) { + if (value.equals(WebComponentUtil.getLocalizedOrOriginPolyStringValue(row.getLabel() != null ? row.getLabel().toPolyString() : null))) { + return originConverter.convertToObject(row.getKey(), locale); + } + } + boolean differentValue = true; + if (baseComponent != null && baseComponent.getModelObject() != null + && baseComponent.getModelObject().equals(value)) { + differentValue = false; + } + + if (differentValue && strict) { + throw new ConversionException("Cannot convert " + value); + } + + return originConverter.convertToObject(value, locale); + + } + + @Override + public String convertToString(C key, Locale arg1) { + if (lookupTable != null) { + for (LookupTableRowType row : lookupTable.getRow()) { + if (key.toString().equals(row.getKey())) { + return WebComponentUtil.getLocalizedOrOriginPolyStringValue(row.getLabel() != null ? row.getLabel().toPolyString() : null); + } + } + } + return key.toString(); + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/TextPopupPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/TextPopupPanel.java index eb6971ea4ac..0c3584125c6 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/TextPopupPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/TextPopupPanel.java @@ -7,6 +7,8 @@ package com.evolveum.midpoint.web.component.search; +import com.evolveum.midpoint.gui.api.component.autocomplete.AutoCompleteTextPanel; +import com.evolveum.midpoint.gui.api.component.autocomplete.LookupTableConverter; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.util.DisplayableValue; @@ -25,6 +27,8 @@ import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.util.convert.ConversionException; +import org.apache.wicket.util.convert.IConverter; import java.io.Serializable; import java.util.*; @@ -99,7 +103,6 @@ private TextField initTextField() { settings.setShowListOnEmptyInput(true); - //TODO: converter return new AutoCompleteTextField(ID_TEXT_INPUT, new PropertyModel<>(getModel(), SearchValue.F_VALUE), settings) { private static final long serialVersionUID = 1L; @@ -108,7 +111,23 @@ protected Iterator getChoices(String input) { return prepareAutoCompleteList(input).iterator(); } + @Override + public IConverter getConverter(Class type) { + IConverter converter = super.getConverter(type); + if (lookup == null) { + return converter; + } + + return new LookupTableConverter(converter, lookup.asObjectable(), this, false){ + @Override + public Object convertToObject(String value, Locale locale) throws ConversionException { + PropertyModel label = new PropertyModel<>(TextPopupPanel.this.getModelObject(), SearchValue.F_LABEL); + label.setObject(value); + return super.convertToObject(value, locale); + } + }; + } }; } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Item.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Item.java index 6dc9622474a..523ddab0f27 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Item.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Item.java @@ -27,6 +27,7 @@ import com.evolveum.midpoint.prism.path.ItemName; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.util.DebugDumpable; +import com.evolveum.midpoint.util.annotation.Experimental; import com.evolveum.midpoint.util.exception.SchemaException; import com.google.common.annotations.VisibleForTesting; @@ -270,6 +271,18 @@ default V getAnyValue(@NotNull ValueSelector selector) { @NotNull Collection getRealValues(); + @Experimental + @NotNull + default Collection getRealValuesOrRawTypes(PrismContext prismContext) { + List rv = new ArrayList<>(); + for (V value : getValues()) { + if (value != null) { + rv.add(value.getRealValueOrRawType(prismContext)); + } + } + return rv; + } + /** * Returns true if the item contains 0 or 1 values and (by definition) is not multivalued. */ @@ -458,12 +471,18 @@ default boolean add(@NotNull V newValue) throws SchemaException { /** * Computes a difference (delta) with the specified item using IGNORE_METADATA_CONSIDER_DIFFERENT_IDS equivalence strategy. + * + * Compares item values only -- does NOT dive into lower levels. */ - ItemDelta diff(Item other); + default ItemDelta diff(Item other) { + return diff(other, ParameterizedEquivalenceStrategy.DEFAULT_FOR_DIFF); + } /** * Computes a difference (delta) with the specified item using given equivalence strategy. * Note this method cannot accept general EquivalenceStrategy here; it needs the parameterized strategy. + * + * Compares item values only -- does NOT dive into lower levels. */ ItemDelta diff(Item other, @NotNull ParameterizedEquivalenceStrategy strategy); diff --git a/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ItemType.java b/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ItemType.java index fffb3df4c4d..139f71ed1fb 100644 --- a/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ItemType.java +++ b/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ItemType.java @@ -94,18 +94,11 @@ public String toString() { '}'; } - public static ItemType fromItem(Item item) { + public static ItemType fromItem(Item item, PrismContext prismContext) { if (item != null) { ItemType rv = new ItemType(); rv.setName(item.getElementName()); - if (item instanceof PrismContainer && (item.getDefinition() == null || ((PrismContainerDefinition) item.getDefinition()).getCompileTimeClass() == null)) { - // a special case -- item.getRealValues() does not work here (TODO generalize this) - for (PrismContainerValue value : ((PrismContainer) item).getValues()) { - rv.value.add(new RawType(value, null, item.getPrismContext())); - } - } else { - rv.value.addAll(item.getRealValues()); - } + rv.value.addAll(item.getRealValuesOrRawTypes(prismContext)); return rv; } else { return null; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/ItemImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/ItemImpl.java index c5544210fb4..45598181049 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/ItemImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/ItemImpl.java @@ -26,6 +26,8 @@ import javax.xml.namespace.QName; +import com.evolveum.midpoint.util.annotation.Experimental; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -488,6 +490,15 @@ public boolean add(@NotNull V newValue, boolean checkUniqueness, EquivalenceStra return values.add(newValue); } + /** + * Adds a given value with no checks, no definition application, and so on. + * For internal use only. + */ + @Experimental + public boolean addForced(@NotNull V newValue) { + return values.add(newValue); + } + public boolean removeAll(Collection newValues) { checkMutable(); boolean changed = false; @@ -581,23 +592,13 @@ public void merge(Item otherItem) throws SchemaException { } } - public ItemDelta diff(Item other) { - return diff(other, ParameterizedEquivalenceStrategy.DEFAULT_FOR_DIFF); - } - public ItemDelta diff(Item other, @NotNull ParameterizedEquivalenceStrategy strategy) { - List itemDeltas = new ArrayList<>(); - diffInternal(other, itemDeltas, strategy); - if (itemDeltas.isEmpty()) { - return null; - } - if (itemDeltas.size() > 1) { - throw new UnsupportedOperationException("Item multi-delta diff is not supported yet"); - } - return itemDeltas.get(0); + List> itemDeltas = new ArrayList<>(); + diffInternal(other, itemDeltas, true, strategy); + return MiscUtil.extractSingleton(itemDeltas); } - void diffInternal(Item other, Collection deltas, + void diffInternal(Item other, Collection deltas, boolean rootValuesOnly, ParameterizedEquivalenceStrategy strategy) { ItemDelta delta = createDelta(); if (other == null) { @@ -621,7 +622,7 @@ void diffInternal(Item other, Collection deltas, boolean found = false; while (iterator.hasNext()) { PrismValueImpl otherValue = (PrismValueImpl) iterator.next(); - if (thisValue.representsSameValue(otherValue, true)) { + if (!rootValuesOnly && thisValue.representsSameValue(otherValue, true)) { found = true; // Matching IDs, look inside to figure out internal deltas ((PrismValueImpl) thisValue).diffMatchingRepresentation(otherValue, deltas, strategy); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerImpl.java index d5793ce169e..72a390c0235 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerImpl.java @@ -689,7 +689,7 @@ public List diffModifications(PrismContainer other) { public List diffModifications(PrismContainer other, ParameterizedEquivalenceStrategy strategy) { List itemDeltas = new ArrayList<>(); - diffInternal(other, itemDeltas, strategy); + diffInternal(other, itemDeltas, false, strategy); return itemDeltas; } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java index 68f17758f99..4a809988491 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java @@ -1058,7 +1058,7 @@ private void diffItems(PrismContainerValue thisValue, PrismContainerValue } } // The "delete" delta will also result from the following diff - ((ItemImpl) thisItem).diffInternal(otherItem, deltas, strategy); + ((ItemImpl) thisItem).diffInternal(otherItem, deltas, false, strategy); } other.getItems(); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismObjectImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismObjectImpl.java index 980a55db2ca..058cb8e860a 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismObjectImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismObjectImpl.java @@ -280,7 +280,7 @@ public ObjectDelta diff(PrismObject other, ParameterizedEquivalenceStrateg objectDelta.setOid(getOid()); Collection itemDeltas = new ArrayList<>(); - diffInternal(other, itemDeltas, strategy); + diffInternal(other, itemDeltas, false, strategy); objectDelta.addModifications(itemDeltas); return objectDelta; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/item/DummyItem.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/item/DummyItem.java index 240d3efc96b..b77f1c34d05 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/item/DummyItem.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/item/DummyItem.java @@ -287,11 +287,6 @@ public final boolean valuesEqual(Collection matchValues, return delegate().valuesEqual(matchValues, comparator); } - public final ItemDelta diff( - Item other) { - return delegate().diff(other); - } - public final ItemDelta diff( Item other, @NotNull ParameterizedEquivalenceStrategy strategy) { diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/BeanUnmarshaller.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/BeanUnmarshaller.java index c6d042f9597..90a452cbc8f 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/BeanUnmarshaller.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/BeanUnmarshaller.java @@ -251,7 +251,11 @@ private T unmarshalFromMapOrHeteroList(@NotNull XNodeImpl mapOrList, @NotNul if (Containerable.class.isAssignableFrom(beanClass)) { // This could have come from inside; note we MUST NOT parse this as PrismValue, because for objects we would lose oid/version - return prismContext.parserFor(mapOrList.toRootXNode()).type(beanClass).parseRealValue(); + return prismContext + .parserFor(mapOrList.toRootXNode()) + .context(pc) + .type(beanClass) + .parseRealValue(); } else if (SearchFilterType.class.isAssignableFrom(beanClass)) { if (mapOrList instanceof MapXNodeImpl) { T bean = (T) unmarshalSearchFilterType((MapXNodeImpl) mapOrList, (Class) beanClass, pc); @@ -1011,7 +1015,10 @@ private Object unmarshalSinglePropValue(XNodeImpl xsubnode, String fieldName, Cl RawType raw = new RawType(xsubnode, prismContext); // FIXME UGLY HACK: parse value if possible if (xsubnode.getTypeQName() != null) { - PrismValue value = prismContext.parserFor(xsubnode.toRootXNode()).parseItemValue(); // TODO what about objects? oid/version will be lost here + PrismValue value = prismContext + .parserFor(xsubnode.toRootXNode()) + .context(pc) + .parseItemValue(); // TODO what about objects? oid/version will be lost here if (value != null && !value.isRaw()) { raw = new RawType(value, xsubnode.getTypeQName(), prismContext); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java index 0bc1ac9c3ac..2085e053082 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java @@ -438,7 +438,18 @@ private PrismProperty parseProperty(@NotNull XNodeImpl node, @NotNull QNa } else if (node instanceof MapXNodeImpl || node instanceof PrimitiveXNodeImpl || node.isHeterogeneousList()) { PrismPropertyValue pval = parsePropertyValue(node, itemDefinition, pc); if (pval != null) { - property.add(pval); + try { + property.add(pval); + } catch (SchemaException e) { + if (pc.isCompat()) { + // Most probably the "apply definition" call while adding the value failed. This occurs for raw + // values with (somewhat) incorrect definitions being added. Overall, this is more a hack than serious + // solution, because we sometimes want to add static-schema-less property values. TODO investigate this. + ((PrismPropertyImpl) property).addForced(pval); + } else { + throw e; + } + } } } else if (node instanceof SchemaXNodeImpl) { SchemaDefinitionType schemaDefType = beanUnmarshaller.unmarshalSchemaDefinitionType((SchemaXNodeImpl) node); diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestParseDiffPatch.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestParseDiffPatch.java index c930120e94f..2a602ab0326 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestParseDiffPatch.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestParseDiffPatch.java @@ -6,9 +6,8 @@ */ package com.evolveum.midpoint.schema; -import static org.testng.AssertJUnit.*; - import static com.evolveum.midpoint.prism.util.PrismTestUtil.getPrismContext; +import static org.testng.AssertJUnit.*; import java.io.File; import java.io.IOException; @@ -16,6 +15,8 @@ import java.util.List; import javax.xml.namespace.QName; +import com.evolveum.midpoint.prism.PrismContainer; + import org.testng.AssertJUnit; import org.testng.annotations.Test; @@ -601,7 +602,7 @@ public void testResourceNsFixUndeclaredPrefixes() throws SchemaException, IOExce resourceFixed.checkConsistence(); // WHEN - String xmlBroken = getPrismContext().serializeObjectToString(resourceBroken, PrismContext.LANG_XML); + String xmlBroken = getPrismContext().xmlSerializer().serialize(resourceBroken); ObjectDelta resourceDelta = resourceBroken.diff(resourceFixed, EquivalenceStrategy.LITERAL_IGNORE_METADATA); // THEN @@ -623,7 +624,7 @@ public void testResourceNsFixUndeclaredPrefixes() throws SchemaException, IOExce PrismObject resourceUpdated = resourceBroken.clone(); resourceDelta.applyTo(resourceUpdated); - String xmlUpdated = getPrismContext().serializeObjectToString(resourceUpdated, PrismContext.LANG_XML); + String xmlUpdated = getPrismContext().xmlSerializer().serialize(resourceUpdated); System.out.println("UPDATED RESOURCE:"); System.out.println(xmlUpdated); assertFalse("__UNDECLARED__ flag in updated resource", xmlUpdated.contains("__UNDECLARED__")); @@ -661,6 +662,7 @@ private void assertModificationPolyStringValue(RawType value, PolyStringType... for (PolyStringType expectedValue : expectedValues) { if (expectedValue.getOrig().equals(valueAsPoly.getOrig()) && expectedValue.getNorm().equals(valueAsPoly.getNorm())) { found = true; + break; } } assertTrue(found); @@ -695,13 +697,13 @@ public void testCampaign() throws SchemaException, IOException { @Test(enabled = false) public void testReplaceModelOperationContext() throws Exception { - PrismObject prismObject = PrismTestUtil.parseObject(new File(TEST_DIR, "task-modelOperationContext-before.xml")); + PrismObject prismObject = PrismTestUtil.parseObject(new File(TEST_DIR, "task-modelOperationContext-before.xml")); - ObjectDelta delta = getPrismContext().deltaFactory().object().createEmptyModifyDelta(TaskType.class, prismObject.getOid() - ); + ObjectDelta delta = getPrismContext().deltaFactory().object().createEmptyModifyDelta(TaskType.class, prismObject.getOid()); + //noinspection unchecked delta.addModificationReplaceContainer(TaskType.F_MODEL_OPERATION_CONTEXT); - PrismObject changed = prismObject.clone(); + PrismObject changed = prismObject.clone(); ItemDeltaCollectionsUtil.applyTo(delta.getModifications(), changed); Collection processedModifications = prismObject.diffModifications(changed, EquivalenceStrategy.LITERAL_IGNORE_METADATA); @@ -778,4 +780,63 @@ public void testSystemConfigurationDiffPlusNarrow() throws Exception { deltaNarrowed.applyTo(workingCopy); PrismAsserts.assertEquals("before + delta (narrowed) is different from after", after, workingCopy); } + @Test + public void testDiffContainerValues() { + UserType user1 = new UserType(getPrismContext()) + .beginAssignment() + .id(1L) + .targetRef("oid-a", RoleType.COMPLEX_TYPE) + .end() + .beginAssignment() + .id(2L) + .targetRef("oid-b", RoleType.COMPLEX_TYPE) + .end(); + UserType user2 = new UserType(getPrismContext()) + .beginAssignment() + .id(3L) + .targetRef("oid-a", RoleType.COMPLEX_TYPE) + .end() + .beginAssignment() + .targetRef("oid-c", RoleType.COMPLEX_TYPE) + .end(); + PrismContainer assignment1 = user1.asPrismObject().findContainer(UserType.F_ASSIGNMENT); + PrismContainer assignment2 = user2.asPrismObject().findContainer(UserType.F_ASSIGNMENT); + ContainerDelta diff = assignment1.diff(assignment2); + + PrismTestUtil.display("assignment1", assignment1); + PrismTestUtil.display("assignment2", assignment2); + PrismTestUtil.display("diff", diff); + + assertEquals("Wrong values to add", assignment2.getValues(), diff.getValuesToAdd()); + assertEquals("Wrong values to delete", assignment1.getValues(), diff.getValuesToDelete()); + //noinspection SimplifiedTestNGAssertion + assertEquals("Wrong values to replace", null, diff.getValuesToReplace()); + } + + @Test + public void testDiffSingleContainerValues() { + UserType user1 = new UserType(getPrismContext()) + .beginActivation() + .validFrom("2020-03-20T15:11:40.936+01:00") + .validTo("2020-03-21T15:11:40.936+01:00") + .end(); + UserType user2 = new UserType(getPrismContext()) + .beginActivation() + .validFrom("2020-02-20T15:11:40.936+01:00") + .validTo("2020-02-21T15:11:40.936+01:00") + .end(); + PrismContainer activation1 = user1.asPrismObject().findContainer(UserType.F_ACTIVATION); + PrismContainer activation2 = user2.asPrismObject().findContainer(UserType.F_ACTIVATION); + + ItemDelta diff = activation1.diff(activation2); + + PrismTestUtil.display("activation1", activation1); + PrismTestUtil.display("activation2", activation2); + PrismTestUtil.display("diff", diff); + + assertEquals("Wrong values to add", activation2.getValues(), diff.getValuesToAdd()); + assertEquals("Wrong values to delete", activation1.getValues(), diff.getValuesToDelete()); + //noinspection SimplifiedTestNGAssertion + assertEquals("Wrong values to replace", null, diff.getValuesToReplace()); + } } diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java index 4c90615311f..26a0836f10e 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/AbstractValueTransformationExpressionEvaluator.java @@ -572,7 +572,7 @@ private String dumpValueCombination(Collection pvalues, Li for (PrismValue pval: pvalues) { SourceTriple sourceTriple = sourceTriplesIterator.next(); sb.append(sourceTriple.getName().getLocalPart()).append('='); - sb.append(pval==null?null:(Object)pval.getRealValue()); + sb.append(pval==null?null:(Object)pval.getRealValueOrRawType(prismContext)); if (sourceTriplesIterator.hasNext()) { sb.append(", "); } diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingImpl.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingImpl.java index 463395d38c8..059164bd6fd 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingImpl.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingImpl.java @@ -462,7 +462,7 @@ private void traceSources() throws SchemaException { for (Source source : sources) { MappingSourceEvaluationTraceType sourceTrace = new MappingSourceEvaluationTraceType(prismContext); sourceTrace.setName(source.getName()); - sourceTrace.setItemDeltaItem(source.toItemDeltaItemType()); + sourceTrace.setItemDeltaItem(source.toItemDeltaItemType(prismContext)); trace.getSource().add(sourceTrace); } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java index 5a9bece4a80..3583056e4d3 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java @@ -30,6 +30,7 @@ import com.evolveum.midpoint.prism.path.*; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.query.*; +import com.evolveum.midpoint.prism.query.builder.S_AtomicFilterExit; import com.evolveum.midpoint.prism.util.CloneUtil; import com.evolveum.midpoint.provisioning.api.*; import com.evolveum.midpoint.repo.api.PreconditionViolationException; @@ -1249,57 +1250,20 @@ public Integer countObjects(Class type, ObjectQuery or @Override @Deprecated - public PrismObject findShadowOwner(String accountOid, Task task, OperationResult parentResult) - throws ObjectNotFoundException, SecurityViolationException, SchemaException, ConfigurationException, ExpressionEvaluationException, CommunicationException { + public PrismObject findShadowOwner( + String accountOid, Task task, OperationResult parentResult) + throws ObjectNotFoundException, SecurityViolationException, SchemaException, + ConfigurationException, ExpressionEvaluationException, CommunicationException { Validate.notEmpty(accountOid, "Account oid must not be null or empty."); Validate.notNull(parentResult, "Result type must not be null."); - enterModelMethod(); - - PrismObject user; - - LOGGER.trace("Listing account shadow owner for account with oid {}.", new Object[]{accountOid}); - - OperationResult result = parentResult.createSubresult(LIST_ACCOUNT_SHADOW_OWNER); - result.addParam("accountOid", accountOid); - - try { - - user = cacheRepositoryService.listAccountShadowOwner(accountOid, result); - result.recordSuccess(); - } catch (ObjectNotFoundException ex) { - LoggingUtils.logException(LOGGER, "Account with oid {} doesn't exists", ex, accountOid); - result.recordFatalError("Account with oid '" + accountOid + "' doesn't exists", ex); - throw ex; - } catch (RuntimeException | Error ex) { - LoggingUtils.logException(LOGGER, "Couldn't list account shadow owner from repository" - + " for account with oid {}", ex, accountOid); - result.recordFatalError("Couldn't list account shadow owner for account with oid '" - + accountOid + "'.", ex); - throw ex; - } finally { - if (LOGGER.isTraceEnabled()) { - LOGGER.trace(result.dump(false)); - } - exitModelMethod(); - result.cleanupResult(); - } - - if (user != null) { - try { - user = user.cloneIfImmutable(); - schemaTransformer.applySchemasAndSecurity(user, null, null,null, task, result); - } catch (SchemaException | SecurityViolationException | ConfigurationException | - ExpressionEvaluationException | ObjectNotFoundException | CommunicationException ex) { - LoggingUtils.logException(LOGGER, "Couldn't list account shadow owner from repository" - + " for account with oid {}", ex, accountOid); - result.recordFatalError("Couldn't list account shadow owner for account with oid '" - + accountOid + "'.", ex); - throw ex; - } - } - - return user; + ObjectQuery query = prismContext.queryFor(UserType.class) + .item(UserType.F_LINK_REF) + .ref(accountOid) + .build(); + SearchResultList> prismObjects = + searchObjects(UserType.class, query, null, task, parentResult); + return MiscUtil.extractSingleton(prismObjects); } @Override diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/InboundProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/InboundProcessor.java index 8ba7d519914..90089b90050 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/InboundProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/InboundProcessor.java @@ -1095,12 +1095,10 @@ private It boolean tolerateTargetValues = !outputDefinition.isSingleValue() || rangeCompletelyDefined; if (targetFocusItem != null) { + LOGGER.trace("Comparing focus item:\n{}\nto should be item:\n{}", + DebugUtil.debugDumpLazily(targetFocusItem, 1), DebugUtil.debugDumpLazily(shouldBeItem, 1)); ItemDelta diffDelta = targetFocusItem.diff(shouldBeItem); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Comparing focus item:\n{}\nto should be item:\n{}\ndiff:\n{} ", - DebugUtil.debugDump(targetFocusItem, 1), DebugUtil.debugDump(shouldBeItem, 1), - DebugUtil.debugDump(diffDelta, 1)); - } + LOGGER.trace("The difference is:\n{}", DebugUtil.debugDumpLazily(diffDelta, 1)); if (diffDelta != null) { // this is probably not correct, as the default for diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/util/mock/MockFactory.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/util/mock/MockFactory.java index 7980f3c35da..d8ebe565910 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/util/mock/MockFactory.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/util/mock/MockFactory.java @@ -6,11 +6,18 @@ */ package com.evolveum.midpoint.model.impl.util.mock; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import javax.xml.namespace.QName; + +import org.jetbrains.annotations.NotNull; + import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; import com.evolveum.midpoint.prism.Containerable; import com.evolveum.midpoint.prism.Objectable; import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; @@ -24,33 +31,25 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.statistics.ConnectorOperationalStatus; import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import org.jetbrains.annotations.NotNull; - -import javax.xml.namespace.QName; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; public class MockFactory { public static ProvisioningService createProvisioningService() { return new ProvisioningService() { @Override - public PrismObject getObject(Class type, String oid, Collection> options, Task task, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + public PrismObject getObject(Class type, String oid, Collection> options, Task task, OperationResult parentResult) { return null; } @Override - public String addObject(PrismObject object, OperationProvisioningScriptsType scripts, ProvisioningOperationOptions options, Task task, OperationResult parentResult) throws ObjectAlreadyExistsException, SchemaException, CommunicationException, ObjectNotFoundException, ConfigurationException, SecurityViolationException, PolicyViolationException, ExpressionEvaluationException { + public String addObject(PrismObject object, OperationProvisioningScriptsType scripts, ProvisioningOperationOptions options, Task task, OperationResult parentResult) { return null; } @Override - public int synchronize(ResourceShadowDiscriminator shadowCoordinates, Task task, TaskPartitionDefinitionType taskPartition, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException, PolicyViolationException, PreconditionViolationException { + public int synchronize(ResourceShadowDiscriminator shadowCoordinates, Task task, TaskPartitionDefinitionType taskPartition, OperationResult parentResult) { return 0; } @@ -61,77 +60,77 @@ public void processAsynchronousUpdates(ResourceShadowDiscriminator shadowCoordin @NotNull @Override - public SearchResultList> searchObjects(Class type, ObjectQuery query, Collection> options, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { - return new SearchResultList(new ArrayList<>(0)); + public SearchResultList> searchObjects(Class type, ObjectQuery query, Collection> options, Task task, OperationResult parentResult) { + return new SearchResultList<>(new ArrayList<>(0)); } @Override - public Integer countObjects(Class type, ObjectQuery query, Collection> options, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + public Integer countObjects(Class type, ObjectQuery query, Collection> options, Task task, OperationResult parentResult) { return null; } @Override - public SearchResultMetadata searchObjectsIterative(Class type, ObjectQuery query, Collection> options, ResultHandler handler, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + public SearchResultMetadata searchObjectsIterative(Class type, ObjectQuery query, Collection> options, ResultHandler handler, Task task, OperationResult parentResult) { return null; } @Override - public String modifyObject(Class type, String oid, Collection modifications, OperationProvisioningScriptsType scripts, ProvisioningOperationOptions options, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, PolicyViolationException, ObjectAlreadyExistsException, ExpressionEvaluationException { + public String modifyObject(Class type, String oid, Collection modifications, OperationProvisioningScriptsType scripts, ProvisioningOperationOptions options, Task task, OperationResult parentResult) { return null; } @Override - public PrismObject deleteObject(Class type, String oid, ProvisioningOperationOptions option, OperationProvisioningScriptsType scripts, Task task, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException, SecurityViolationException, PolicyViolationException, ExpressionEvaluationException { + public PrismObject deleteObject(Class type, String oid, ProvisioningOperationOptions option, OperationProvisioningScriptsType scripts, Task task, OperationResult parentResult) { return null; } @Override - public Object executeScript(String resourceOid, ProvisioningScriptType script, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ObjectAlreadyExistsException, ExpressionEvaluationException { + public Object executeScript(String resourceOid, ProvisioningScriptType script, Task task, OperationResult parentResult) { return null; } @Override - public OperationResult testResource(String resourceOid, Task task) throws ObjectNotFoundException { + public OperationResult testResource(String resourceOid, Task task) { return null; } @Override - public Set discoverConnectors(ConnectorHostType hostType, OperationResult parentResult) throws CommunicationException { + public Set discoverConnectors(ConnectorHostType hostType, OperationResult parentResult) { return null; } @Override - public List getConnectorOperationalStatus(String resourceOid, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + public List getConnectorOperationalStatus(String resourceOid, Task task, OperationResult parentResult) { return null; } @Override - public List> listResourceObjects(String resourceOid, QName objectClass, ObjectPaging paging, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + public List> listResourceObjects(String resourceOid, QName objectClass, ObjectPaging paging, Task task, OperationResult parentResult) { return null; } @Override - public void refreshShadow(PrismObject shadow, ProvisioningOperationOptions options, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ObjectAlreadyExistsException, SecurityViolationException, ExpressionEvaluationException { + public void refreshShadow(PrismObject shadow, ProvisioningOperationOptions options, Task task, OperationResult parentResult) { } @Override - public void applyDefinition(ObjectDelta delta, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + public void applyDefinition(ObjectDelta delta, Task task, OperationResult parentResult) { } @Override - public void applyDefinition(ObjectDelta delta, Objectable object, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + public void applyDefinition(ObjectDelta delta, Objectable object, Task task, OperationResult parentResult) { } @Override - public void applyDefinition(PrismObject object, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + public void applyDefinition(PrismObject object, Task task, OperationResult parentResult) { } @Override - public void applyDefinition(Class type, ObjectQuery query, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + public void applyDefinition(Class type, ObjectQuery query, Task task, OperationResult parentResult) { } @@ -151,7 +150,7 @@ public void postInit(OperationResult parentResult) { } @Override - public ConstraintsCheckingResult checkConstraints(RefinedObjectClassDefinition shadowDefinition, PrismObject shadowObject, PrismObject shadowObjectOld, ResourceType resourceType, String shadowOid, ResourceShadowDiscriminator resourceShadowDiscriminator, ConstraintViolationConfirmer constraintViolationConfirmer, ConstraintsCheckingStrategyType strategy, Task task, OperationResult parentResult) throws CommunicationException, ObjectAlreadyExistsException, SchemaException, SecurityViolationException, ConfigurationException, ObjectNotFoundException, ExpressionEvaluationException { + public ConstraintsCheckingResult checkConstraints(RefinedObjectClassDefinition shadowDefinition, PrismObject shadowObject, PrismObject shadowObjectOld, ResourceType resourceType, String shadowOid, ResourceShadowDiscriminator resourceShadowDiscriminator, ConstraintViolationConfirmer constraintViolationConfirmer, ConstraintsCheckingStrategyType strategy, Task task, OperationResult parentResult) { return null; } @@ -166,7 +165,7 @@ public void exitConstraintsCheckerCache() { } @Override - public ItemComparisonResult compare(Class type, String oid, ItemPath path, T expectedValue, Task task, OperationResult result) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException, EncryptionException { + public ItemComparisonResult compare(Class type, String oid, ItemPath path, T expectedValue, Task task, OperationResult result) { return null; } @@ -186,12 +185,12 @@ public static RepositoryService createRepositoryService() { return new RepositoryService() { @Override @NotNull - public PrismObject getObject(Class type, String oid, Collection> options, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { + public PrismObject getObject(Class type, String oid, Collection> options, OperationResult parentResult) { throw new UnsupportedOperationException(); } @Override - public String getVersion(Class type, String oid, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { + public String getVersion(Class type, String oid, OperationResult parentResult) { return null; } @@ -201,77 +200,72 @@ public int countContainers(Class type, ObjectQuery } @Override - public String addObject(PrismObject object, RepoAddOptions options, OperationResult parentResult) throws ObjectAlreadyExistsException, SchemaException { + public String addObject(PrismObject object, RepoAddOptions options, OperationResult parentResult) { return null; } @NotNull @Override - public SearchResultList> searchObjects(Class type, ObjectQuery query, Collection> options, OperationResult parentResult) throws SchemaException { - return new SearchResultList(new ArrayList<>(0)); + public SearchResultList> searchObjects(Class type, ObjectQuery query, Collection> options, OperationResult parentResult) { + return new SearchResultList<>(new ArrayList<>(0)); } @Override - public SearchResultList searchContainers(Class type, ObjectQuery query, Collection> options, OperationResult parentResult) throws SchemaException { - return new SearchResultList(new ArrayList<>(0)); + public SearchResultList searchContainers(Class type, ObjectQuery query, Collection> options, OperationResult parentResult) { + return new SearchResultList<>(new ArrayList<>(0)); } @Override - public SearchResultMetadata searchObjectsIterative(Class type, ObjectQuery query, ResultHandler handler, Collection> options, boolean strictlySequential, OperationResult parentResult) throws SchemaException { + public SearchResultMetadata searchObjectsIterative(Class type, ObjectQuery query, ResultHandler handler, Collection> options, boolean strictlySequential, OperationResult parentResult) { return null; } @Override - public int countObjects(Class type, ObjectQuery query, Collection> options, OperationResult parentResult) throws SchemaException { + public int countObjects(Class type, ObjectQuery query, Collection> options, OperationResult parentResult) { return 0; } @Override - public int countObjects(Class type, ObjectQuery query, OperationResult parentResult) throws SchemaException { + public int countObjects(Class type, ObjectQuery query, OperationResult parentResult) { return 0; } @Override - public boolean isAnySubordinate(String upperOrgOid, Collection lowerObjectOids) throws SchemaException { + public boolean isAnySubordinate(String upperOrgOid, Collection lowerObjectOids) { return false; } @Override - public boolean isDescendant(PrismObject object, String orgOid) throws SchemaException { + public boolean isDescendant(PrismObject object, String orgOid) { return false; } @Override - public boolean isAncestor(PrismObject object, String oid) throws SchemaException { + public boolean isAncestor(PrismObject object, String oid) { return false; } @NotNull @Override - public ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { + public ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, OperationResult parentResult) { return null; } @NotNull @Override - public ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, RepoModifyOptions options, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { + public ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, RepoModifyOptions options, OperationResult parentResult) { return null; } @NotNull @Override - public ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, ModificationPrecondition precondition, RepoModifyOptions options, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException, PreconditionViolationException { + public ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, ModificationPrecondition precondition, RepoModifyOptions options, OperationResult parentResult) { return null; } @NotNull @Override - public DeleteObjectResult deleteObject(Class type, String oid, OperationResult parentResult) throws ObjectNotFoundException { - return null; - } - - @Override - public PrismObject listAccountShadowOwner(String accountOid, OperationResult parentResult) throws ObjectNotFoundException { + public DeleteObjectResult deleteObject(Class type, String oid, OperationResult parentResult) { return null; } @@ -281,17 +275,12 @@ public PrismObject searchShadowOwner(String shadowOid, } @Override - public List> listResourceObjectShadows(String resourceOid, Class resourceObjectShadowType, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { - return null; - } - - @Override - public long advanceSequence(String oid, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { + public long advanceSequence(String oid, OperationResult parentResult) { return 0; } @Override - public void returnUnusedValuesToSequence(String oid, Collection unusedValues, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { + public void returnUnusedValuesToSequence(String oid, Collection unusedValues, OperationResult parentResult) { } @@ -316,7 +305,7 @@ public RepositoryQueryDiagResponse executeQueryDiagnostics(RepositoryQueryDiagRe } @Override - public boolean selectorMatches(ObjectSelectorType objectSelector, PrismObject object, ObjectFilterExpressionEvaluator filterEvaluator, Trace logger, String logMessagePrefix) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { + public boolean selectorMatches(ObjectSelectorType objectSelector, PrismObject object, ObjectFilterExpressionEvaluator filterEvaluator, Trace logger, String logMessagePrefix) { return false; } @@ -331,7 +320,7 @@ public FullTextSearchConfigurationType getFullTextSearchConfiguration() { } @Override - public void postInit(OperationResult result) throws SchemaException { + public void postInit(OperationResult result) { } @@ -351,7 +340,7 @@ public boolean hasConflict(ConflictWatcher watcher, OperationResult result) { } @Override - public void addDiagnosticInformation(Class type, String oid, DiagnosticInformationType information, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { + public void addDiagnosticInformation(Class type, String oid, DiagnosticInformationType information, OperationResult parentResult) { } @@ -395,7 +384,7 @@ public void unregisterNotificationListener(ResourceEventListener listener) { } @Override - public void notifyEvent(ResourceEventDescription eventDescription, Task task, OperationResult parentResult) throws SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ObjectNotFoundException, GenericConnectorException, ObjectAlreadyExistsException, ExpressionEvaluationException, PolicyViolationException { + public void notifyEvent(ResourceEventDescription eventDescription, Task task, OperationResult parentResult) throws GenericConnectorException { } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/mapping/TestMappingInbound.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/mapping/TestMappingInbound.java index b2f05c86dd8..6f7a3cb015e 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/mapping/TestMappingInbound.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/mapping/TestMappingInbound.java @@ -15,11 +15,11 @@ import java.nio.charset.StandardCharsets; import javax.xml.namespace.QName; -import com.evolveum.midpoint.prism.Containerable; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.prism.ValueSelector; +import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.path.ItemName; +import com.evolveum.midpoint.prism.path.ItemPath; + import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; @@ -29,7 +29,6 @@ import com.evolveum.icf.dummy.resource.DummySyncStyle; import com.evolveum.midpoint.common.refinery.RefinedResourceSchemaImpl; import com.evolveum.midpoint.model.api.ModelExecuteOptions; -import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ChangeType; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.schema.constants.MidPointConstants; @@ -65,16 +64,21 @@ public class TestMappingInbound extends AbstractMappingTest { private static final String ACCOUNT_LEELOO_PROOF_STRANGE = "Hereby and hèrěnow\nThis is a multi-line claim\nwith a sôme of špecial chäracters\nAnd even some CRLF file endings\r\nLike this\r\nAnd to be completely nuts, even some LFRC\n\rThis does not really proves anything\n It is just trying to reproduce the problem\nIn addition to be quite long\nand ugly\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident,\nsunt in culpa qui officia deserunt mollit anim id est laborum.\nAnd so on …"; private static final String ACCOUNT_RISKY_USERNAME = "risky"; + private static final String ACCOUNT_GDPR_USERNAME = "gdpr"; private static final File TASK_LIVE_SYNC_DUMMY_TEA_GREEN_FILE = new File(TEST_DIR, "task-dumy-tea-green-livesync.xml"); private static final String TASK_LIVE_SYNC_DUMMY_TEA_GREEN_OID = "10000000-0000-0000-5555-55550000c404"; private static final String LOCKER_BIG_SECRET = "BIG secret"; + private static final ItemName DATA_PROTECTION = new ItemName(NS_PIRACY, "dataProtection"); + private static final ItemName RISK_VECTOR = new ItemName(NS_PIRACY, "riskVector"); private static final ItemName RISK = new ItemName(NS_PIRACY, "risk"); private static final ItemName VALUE = new ItemName(NS_PIRACY, "value"); + private static final String DUMMY_ACCOUNT_ATTRIBUTE_CONTROLLER_NAME = "controllerName"; + private ProtectedStringType mancombLocker; private String userLeelooOid; @@ -93,6 +97,8 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti DUMMY_ACCOUNT_ATTRIBUTE_PROOF_NAME, String.class, false, false); controller.addAttrDef(controller.getDummyResource().getAccountObjectClass(), DUMMY_ACCOUNT_ATTRIBUTE_TREASON_RISK_NAME, Integer.class, false, false); + controller.addAttrDef(controller.getDummyResource().getAccountObjectClass(), + DUMMY_ACCOUNT_ATTRIBUTE_CONTROLLER_NAME, String.class, false, false); controller.setSyncStyle(DummySyncStyle.SMART); }, initTask, initResult); @@ -116,7 +122,7 @@ public void test010SanitySchema() throws Exception { display("Parsed resource schema (tea-green)", returnedSchema); ObjectClassComplexTypeDefinition accountDef = getDummyResourceController(RESOURCE_DUMMY_TEA_GREEN_NAME) .assertDummyResourceSchemaSanityExtended(returnedSchema, resourceType, false, - DummyResourceContoller.PIRATE_SCHEMA_NUMBER_OF_DEFINITIONS + 3); // MID-5197 + DummyResourceContoller.PIRATE_SCHEMA_NUMBER_OF_DEFINITIONS + 4); // MID-5197 ResourceAttributeDefinition lockerDef = accountDef.findAttributeDefinition(DUMMY_ACCOUNT_ATTRIBUTE_LOCKER_NAME); assertNotNull("No locker attribute definition", lockerDef); @@ -751,6 +757,55 @@ public void test500UserRiskVector() throws Exception { .assertPropertyEquals(VALUE, 1); } + /** + * MID-6129 + */ + @Test + public void test510UserDataProtection() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + dummyAuditService.clear(); + + UserType user = new UserType(prismContext) + .name(ACCOUNT_GDPR_USERNAME); + DataProtectionType protection = new DataProtectionType(prismContext) + .controllerName("controller") + .controllerContact("controller@evolveum.com"); + PrismContainerDefinition protectionDef = + user.asPrismObject().getDefinition().findContainerDefinition(ItemPath.create(UserType.F_EXTENSION, DATA_PROTECTION)); + PrismContainer protectionContainer = protectionDef.instantiate(); + //noinspection unchecked + protectionContainer.add(protection.asPrismContainerValue()); + user.asPrismObject().addExtensionItem(protectionContainer); + + addObject(user.asPrismObject(), task, result); + + DummyAccount account = new DummyAccount(ACCOUNT_GDPR_USERNAME); + account.setEnabled(true); + account.addAttributeValues(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_FULLNAME_NAME, "GDPR"); + account.addAttributeValue(DUMMY_ACCOUNT_ATTRIBUTE_CONTROLLER_NAME, "new-controller"); + getDummyResource(RESOURCE_DUMMY_TEA_GREEN_NAME).addAccount(account); + + when(); + + modelService.importFromResource(RESOURCE_DUMMY_TEA_GREEN_OID, new QName(MidPointConstants.NS_RI, SchemaConstants.ACCOUNT_OBJECT_CLASS_LOCAL_NAME), task, result); + waitForTaskFinish(task, true); + + then(); + + assertSuccess(task.getResult()); + + assertUserAfterByUsername(ACCOUNT_GDPR_USERNAME) + .assertExtensionItems(1) + .extensionContainer(DATA_PROTECTION) + .assertSize(1) + .value(0) + .assertPropertyEquals(DataProtectionType.F_CONTROLLER_NAME, "new-controller") + .assertPropertyEquals(DataProtectionType.F_CONTROLLER_CONTACT, "new-controller@evolveum.com"); + + } + protected void importSyncTask() throws FileNotFoundException { importObjectFromFile(TASK_LIVE_SYNC_DUMMY_TEA_GREEN_FILE); } diff --git a/model/model-intest/src/test/resources/mapping/resource-dummy-tea-green.xml b/model/model-intest/src/test/resources/mapping/resource-dummy-tea-green.xml index 96a9a93c32e..abfe77f59f1 100644 --- a/model/model-intest/src/test/resources/mapping/resource-dummy-tea-green.xml +++ b/model/model-intest/src/test/resources/mapping/resource-dummy-tea-green.xml @@ -168,6 +168,11 @@ An authoritative resource, used to test for MID-2100 (inbound mappings acting bo + + + @@ -179,6 +184,30 @@ An authoritative resource, used to test for MID-2100 (inbound mappings acting bo + + ri:controllerName + + strong + + + + + extension/dataProtection + + + + + + 5 diff --git a/model/model-intest/src/test/resources/schema/piracy.xsd b/model/model-intest/src/test/resources/schema/piracy.xsd index 436a65df5bd..0b2bdd2caa6 100644 --- a/model/model-intest/src/test/resources/schema/piracy.xsd +++ b/model/model-intest/src/test/resources/schema/piracy.xsd @@ -15,6 +15,7 @@ xmlns:xsd="http://www.w3.org/2001/XMLSchema"> + @@ -104,6 +105,8 @@ + + diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/AccountOperationListener.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/AccountOperationListener.java index a73cda7b8c0..d60ae19a054 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/AccountOperationListener.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/AccountOperationListener.java @@ -7,31 +7,35 @@ package com.evolveum.midpoint.notifications.impl; +import javax.annotation.PostConstruct; + +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.notifications.api.NotificationManager; import com.evolveum.midpoint.notifications.api.OperationStatus; import com.evolveum.midpoint.notifications.impl.events.ResourceObjectEventImpl; +import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.provisioning.api.ChangeNotificationDispatcher; import com.evolveum.midpoint.provisioning.api.ResourceOperationDescription; import com.evolveum.midpoint.provisioning.api.ResourceOperationListener; import com.evolveum.midpoint.repo.api.RepositoryService; +import com.evolveum.midpoint.schema.SearchResultList; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.MiscUtil; +import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; - /** * Converts provisioning events into notification events. */ @@ -42,6 +46,7 @@ public class AccountOperationListener implements ResourceOperationListener { private static final String DOT_CLASS = AccountOperationListener.class.getName() + "."; + @Autowired private PrismContext prismContext; @Autowired private LightweightIdentifierGenerator lightweightIdentifierGenerator; @Autowired private ChangeNotificationDispatcher provisioningNotificationDispatcher; @Autowired private NotificationManager notificationManager; @@ -138,9 +143,9 @@ private void executeNotifyAny(OperationStatus status, ResourceOperationDescripti @NotNull private ResourceObjectEventImpl createRequest(OperationStatus status, - ResourceOperationDescription operationDescription, - Task task, - OperationResult result) { + ResourceOperationDescription operationDescription, + Task task, + OperationResult result) { ResourceObjectEventImpl event = new ResourceObjectEventImpl(lightweightIdentifierGenerator, operationDescription, status); @@ -173,10 +178,17 @@ private PrismObject findRequestee(String shadowOid, Task task, Operati return task.getRequestee(); } else if (shadowOid != null) { try { - PrismObject user = cacheRepositoryService.listAccountShadowOwner(shadowOid, result); + ObjectQuery query = prismContext.queryFor(UserType.class) + .item(UserType.F_LINK_REF) + .ref(shadowOid) + .build(); + SearchResultList> prismObjects = + cacheRepositoryService.searchObjects(UserType.class, query, null, result); + PrismObject user = MiscUtil.extractSingleton(prismObjects); + LOGGER.trace("listAccountShadowOwner for shadow {} yields {}", shadowOid, user); return user; - } catch (ObjectNotFoundException e) { + } catch (SchemaException e) { LOGGER.trace("There's a problem finding account {}", shadowOid, e); return null; } diff --git a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java index a3ad10082e3..27a849ec943 100644 --- a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java +++ b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java @@ -7,42 +7,20 @@ package com.evolveum.midpoint.repo.api; import java.util.Collection; -import java.util.List; -import javax.xml.namespace.QName; - -import com.evolveum.midpoint.repo.api.perf.PerformanceMonitor; import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.prism.Containerable; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.repo.api.perf.PerformanceMonitor; import com.evolveum.midpoint.repo.api.query.ObjectFilterExpressionEvaluator; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.RepositoryDiag; -import com.evolveum.midpoint.schema.RepositoryQueryDiagRequest; -import com.evolveum.midpoint.schema.RepositoryQueryDiagResponse; -import com.evolveum.midpoint.schema.ResultHandler; -import com.evolveum.midpoint.schema.SearchResultList; -import com.evolveum.midpoint.schema.SearchResultMetadata; -import com.evolveum.midpoint.schema.SelectorOptions; +import com.evolveum.midpoint.schema.*; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.util.exception.CommunicationException; -import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.xml.ns._public.common.common_3.DiagnosticInformationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.FullTextSearchConfigurationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectSelectorType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; /** *

Identity Repository Interface.

@@ -51,7 +29,7 @@ *
  • Status: public
  • *
  • Stability: stable
  • * - * @version 3.1.1 + * * @author Radovan Semancik *

    * This service provides repository for objects that are commonly found @@ -125,6 +103,7 @@ *

  • TODO: task coordination
  • * *

    + * @version 3.1.1 */ public interface RepositoryService { @@ -141,7 +120,6 @@ public interface RepositoryService { String SEARCH_OBJECTS = CLASS_NAME_WITH_DOT + "searchObjects"; String SEARCH_CONTAINERS = CLASS_NAME_WITH_DOT + "searchContainers"; String COUNT_CONTAINERS = CLASS_NAME_WITH_DOT + "countContainers"; - String LIST_RESOURCE_OBJECT_SHADOWS = CLASS_NAME_WITH_DOT + "listResourceObjectShadows"; String MODIFY_OBJECT = CLASS_NAME_WITH_DOT + "modifyObject"; String COUNT_OBJECTS = CLASS_NAME_WITH_DOT + "countObjects"; String GET_VERSION = CLASS_NAME_WITH_DOT + "getVersion"; @@ -160,7 +138,6 @@ public interface RepositoryService { String OP_DELETE_OBJECT = "deleteObject"; String OP_COUNT_OBJECTS = "countObjects"; String OP_MODIFY_OBJECT = "modifyObject"; - String OP_LIST_RESOURCE_OBJECT_SHADOWS = "listResourceObjectShadows"; String OP_GET_VERSION = "getVersion"; String OP_IS_ANY_SUBORDINATE = "isAnySubordinate"; String OP_ADVANCE_SEQUENCE = "advanceSequence"; @@ -168,31 +145,23 @@ public interface RepositoryService { String OP_EXECUTE_QUERY_DIAGNOSTICS = "executeQueryDiagnostics"; String OP_GET_OBJECT = "getObject"; String OP_SEARCH_SHADOW_OWNER = "searchShadowOwner"; - String OP_LIST_ACCOUNT_SHADOW_OWNER = "listAccountShadowOwner"; String OP_SEARCH_OBJECTS = "searchObjects"; String OP_SEARCH_OBJECTS_ITERATIVE = "searchObjectsIterative"; String OP_FETCH_EXT_ITEMS = "fetchExtItems"; /** * Returns object for provided OID. - * + *

    * Must fail if object with the OID does not exists. * - * @param oid - * OID of the object to get - * @param parentResult - * parent OperationResult (in/out) + * @param oid OID of the object to get + * @param parentResult parent OperationResult (in/out) * @return Object fetched from repository - * - * @throws ObjectNotFoundException - * requested object does not exist - * @throws SchemaException - * error dealing with storage schema - * @throws IllegalArgumentException - * wrong OID format, etc. + * @throws ObjectNotFoundException requested object does not exist + * @throws SchemaException error dealing with storage schema + * @throws IllegalArgumentException wrong OID format, etc. */ - @NotNull - PrismObject getObject(Class type, String oid, Collection> options, + @NotNull PrismObject getObject(Class type, String oid, Collection> options, OperationResult parentResult) throws ObjectNotFoundException, SchemaException; @@ -203,24 +172,18 @@ PrismObject getObject(Class type, String oid, Colle /** * Returns object version for provided OID. - * + *

    * Must fail if object with the OID does not exists. - * + *

    * This is a supposed to be a very lightweight and cheap operation. It is used to support * efficient caching of expensive objects. * - * @param oid - * OID of the object to get - * @param parentResult - * parent OperationResult (in/out) + * @param oid OID of the object to get + * @param parentResult parent OperationResult (in/out) * @return Object version - * - * @throws ObjectNotFoundException - * requested object does not exist - * @throws SchemaException - * error dealing with storage schema - * @throws IllegalArgumentException - * wrong OID format, etc. + * @throws ObjectNotFoundException requested object does not exist + * @throws SchemaException error dealing with storage schema + * @throws IllegalArgumentException wrong OID format, etc. */ String getVersion(Class type, String oid, OperationResult parentResult) throws ObjectNotFoundException, SchemaException; @@ -252,23 +215,16 @@ int countContainers(Class type, ObjectQuery query, * Note: no need for explicit type parameter here. The object parameter contains the information. *

    * - * @param object - * object to create - * @param parentResult - * parent OperationResult (in/out) + * @param object object to create + * @param parentResult parent OperationResult (in/out) * @return OID assigned to the created object - * - * @throws ObjectAlreadyExistsException - * object with specified identifiers already exists, cannot add - * @throws SchemaException - * error dealing with storage schema, e.g. schema violation - * @throws IllegalArgumentException - * wrong OID format, etc. + * @throws ObjectAlreadyExistsException object with specified identifiers already exists, cannot add + * @throws SchemaException error dealing with storage schema, e.g. schema violation + * @throws IllegalArgumentException wrong OID format, etc. */ String addObject(PrismObject object, RepoAddOptions options, OperationResult parentResult) throws ObjectAlreadyExistsException, SchemaException; - /** *

    Search for objects in the repository.

    *

    If no search criteria specified, list of all objects of specified type is returned.

    @@ -284,21 +240,15 @@ String addObject(PrismObject object, RepoAddOptions op * specified in the query. *

    * - * @param query - * search query - * @param parentResult - * parent OperationResult (in/out) + * @param query search query + * @param parentResult parent OperationResult (in/out) * @return all objects of specified type that match search criteria (subject - * to paging) - * - * @throws IllegalArgumentException - * wrong object type - * @throws SchemaException - * unknown property used in search query + * to paging) + * @throws IllegalArgumentException wrong object type + * @throws SchemaException unknown property used in search query */ - @NotNull - SearchResultList> searchObjects(Class type, ObjectQuery query, + @NotNull SearchResultList> searchObjects(Class type, ObjectQuery query, Collection> options, OperationResult parentResult) throws SchemaException; @@ -325,46 +275,38 @@ SearchResultList searchContainers(Class type, Ob * specified in the query. *

    * - * @param query - * search query - * @param handler - * result handler - * @param strictlySequential - * takes care not to skip any object nor to process objects more than once; see below - * @param parentResult - * parent OperationResult (in/out) + * @param query search query + * @param handler result handler + * @param strictlySequential takes care not to skip any object nor to process objects more than once; see below + * @param parentResult parent OperationResult (in/out) * @return all objects of specified type that match search criteria (subject to paging) - * - * @throws IllegalArgumentException - * wrong object type - * @throws SchemaException - * unknown property used in search query - * + * @throws IllegalArgumentException wrong object type + * @throws SchemaException unknown property used in search query + *

    * A note related to iteration method: - * + *

    * There are three iteration methods (see IterationMethodType): * - SINGLE_TRANSACTION: Fetches objects in single DB transaction. Not supported for all DBMSs. * - SIMPLE_PAGING: Uses the "simple paging" method: takes objects (e.g.) numbered 0 to 49, then 50 to 99, - * then 100 to 149, and so on. The disadvantage is that if the order of objects is changed - * during operation (e.g. by inserting/deleting some of them) then some objects can be - * processed multiple times, where others can be skipped. + * then 100 to 149, and so on. The disadvantage is that if the order of objects is changed + * during operation (e.g. by inserting/deleting some of them) then some objects can be + * processed multiple times, where others can be skipped. * - STRICTLY_SEQUENTIAL_PAGING: Uses the "strictly sequential paging" method: sorting returned objects by OID. This - * is (almost) reliable in such a way that no object would be skipped. However, custom - * paging cannot be used in this mode. - * + * is (almost) reliable in such a way that no object would be skipped. However, custom + * paging cannot be used in this mode. + *

    * If GetOperationOptions.iterationMethod is specified, it is used without any further considerations. * Otherwise, the repository configuration determines whether to use SINGLE_TRANSACTION or a paging. In the latter case, * strictlySequential flag determines between SIMPLE_PAGING (if false) and STRICTLY_SEQUENTIAL_PAGING (if true). - * + *

    * If explicit GetOperationOptions.iterationMethod is not provided, and paging is prescribed, and strictlySequential flag * is true and client-provided paging conflicts with the paging used by the iteration method, a warning is issued, and * iteration method is switched to SIMPLE_PAGING. - * + *

    * Sources of conflicts: - * - ordering is specified - * - offset is specified + * - ordering is specified + * - offset is specified * (limit is not a problem) - * */ SearchResultMetadata searchObjectsIterative(Class type, ObjectQuery query, @@ -380,23 +322,19 @@ SearchResultMetadata searchObjectsIterative(Class type * specified in the query. *

    * - * @param query - * search query - * @param parentResult - * parent OperationResult (in/out) + * @param query search query + * @param parentResult parent OperationResult (in/out) * @return count of objects of specified type that match search criteria (subject - * to paging) - * - * @throws IllegalArgumentException - * wrong object type - * @throws SchemaException - * unknown property used in search query + * to paging) + * @throws IllegalArgumentException wrong object type + * @throws SchemaException unknown property used in search query */ int countObjects(Class type, ObjectQuery query, Collection> options, OperationResult parentResult) throws SchemaException; - @Deprecated // use the version with options instead + @Deprecated + // use the version with options instead int countObjects(Class type, ObjectQuery query, OperationResult parentResult) throws SchemaException; @@ -421,34 +359,25 @@ int countObjects(Class type, ObjectQuery query, Operat * underlying schema of the storage system or the schema enforced by the * implementation. *

    - * + *

    * TODO: optimistic locking - * + *

    * Note: the precondition is checked only if actual modification is going to take place * (not e.g. if the list of modifications is empty). * - * @param parentResult - * parent OperationResult (in/out) - * - * @throws ObjectNotFoundException - * specified object does not exist - * @throws SchemaException - * resulting object would violate the schema - * @throws ObjectAlreadyExistsException - * if resulting object would have name which already exists in another object of the same type - * @throws IllegalArgumentException - * wrong OID format, described change is not applicable + * @param parentResult parent OperationResult (in/out) + * @throws ObjectNotFoundException specified object does not exist + * @throws SchemaException resulting object would violate the schema + * @throws ObjectAlreadyExistsException if resulting object would have name which already exists in another object of the same type + * @throws IllegalArgumentException wrong OID format, described change is not applicable */ - @NotNull - ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, OperationResult parentResult) + @NotNull ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException; - @NotNull - ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, RepoModifyOptions options, OperationResult parentResult) + @NotNull ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, RepoModifyOptions options, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException; - @NotNull - ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, + @NotNull ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, ModificationPrecondition precondition, RepoModifyOptions options, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException, PreconditionViolationException; @@ -458,51 +387,12 @@ ModifyObjectResult modifyObject(Class type, String * Must fail if object with specified OID does not exists. Should be atomic. *

    * - * @param oid - * OID of object to delete - * @param parentResult - * parent OperationResult (in/out) - * - * @throws ObjectNotFoundException - * specified object does not exist - * @throws IllegalArgumentException - * wrong OID format, described change is not applicable + * @param oid OID of object to delete + * @param parentResult parent OperationResult (in/out) + * @throws ObjectNotFoundException specified object does not exist + * @throws IllegalArgumentException wrong OID format, described change is not applicable */ - @NotNull - DeleteObjectResult deleteObject(Class type, String oid, OperationResult parentResult) throws ObjectNotFoundException; - - /** - *

    Returns the User object representing owner of specified account (account - * shadow).

    - *

    - * May return null if there is no owner specified for the account. - *

    - * May only be called with OID of AccountShadow object. - *

    - * Implements the backward "owns" association between account shadow and - * user. Forward association is implemented by property "account" of user - * object. - *

    - * This is a "list" operation even though it may return at most one owner. - * However the operation implies searching the repository for an owner, - * which may be less efficient that following a direct association. Hence it - * is called "list" to indicate that there may be non-negligible overhead. - *

    - * - * @param accountOid - * OID of account shadow - * @param parentResult - * parentResult parent OperationResult (in/out) - * @return User object representing owner of specified account - * - * @throws ObjectNotFoundException - * specified object does not exist - * @throws IllegalArgumentException - * wrong OID format - */ - @Deprecated - PrismObject listAccountShadowOwner(String accountOid, OperationResult parentResult) - throws ObjectNotFoundException; + @NotNull DeleteObjectResult deleteObject(Class type, String oid, OperationResult parentResult) throws ObjectNotFoundException; /** *

    Returns the object representing owner of specified shadow.

    @@ -527,51 +417,14 @@ PrismObject listAccountShadowOwner(String accountOid, OperationResult * will not be able to do proper cleanup. *

    * - * @param shadowOid - * OID of shadow - * @param parentResult - * parentResult parent OperationResult (in/out) + * @param shadowOid OID of shadow + * @param parentResult parentResult parent OperationResult (in/out) * @return Object representing owner of specified account (subclass of FocusType) - * - * @throws IllegalArgumentException - * wrong OID format + * @throws IllegalArgumentException wrong OID format */ PrismObject searchShadowOwner(String shadowOid, Collection> options, OperationResult parentResult); /** - *

    Search for resource object shadows of a specified type that belong to the - * specified resource.

    - *

    - * Returns a list of such object shadows or empty list - * if nothing was found. - *

    - * Implements the backward "has" association between resource and resource - * object shadows. Forward association is implemented by property "resource" - * of resource object shadow. - *

    - * May only be called with OID of Resource object. - *

    - * - * @param resourceOid - * OID of resource definition (ResourceType) - * @param parentResult - * parentResult parent OperationResult (in/out) - * @return resource object shadows of a specified type from specified - * resource - * - * @throws ObjectNotFoundException - * specified object does not exist - * @throws SchemaException - * found object is not type of {@link ShadowType} - * @throws IllegalArgumentException - * wrong OID format - */ - List> listResourceObjectShadows(String resourceOid, - Class resourceObjectShadowType, OperationResult parentResult) throws ObjectNotFoundException, - SchemaException; - - /** - * * This operation is guaranteed to be atomic. If two threads or even two nodes request a value from * the same sequence at the same time then different values will be returned. * @@ -584,7 +437,6 @@ List> listResourceObjectShadows(String res long advanceSequence(String oid, OperationResult parentResult) throws ObjectNotFoundException, SchemaException; /** - * * The sequence may ignore the values, e.g. if value re-use is disabled or when the list of * unused values is full. In such a case the values will be ignored silently and no error is indicated. * @@ -600,14 +452,14 @@ List> listResourceObjectShadows(String res RepositoryDiag getRepositoryDiag(); /** - * Runs a short, non-descructive repository self test. + * Runs a short, non-destructive repository self test. * This methods should never throw a (checked) exception. All the results * should be recorded under the provided result structure (including fatal errors). - * + *

    * This should implement ONLY self-tests that are IMPLEMENTATION-SPECIFIC. It must not * implement self-tests that are generic and applies to all repository implementations. * Such self-tests must be implemented in higher layers. - * + *

    * If the repository has no self-tests then the method should return immediately * without changing the result structure. It must not throw an exception in this case. */ @@ -617,14 +469,14 @@ List> listResourceObjectShadows(String res * Checks a closure for consistency, repairing any problems found. * This methods should never throw a (checked) exception. All the results * should be in the returned result structure (including fatal errors). - * + *

    * The current implementation expects closure to be of reasonable size - so * it could be fetched into main memory as well as recomputed online * (perhaps up to ~250K entries). In future, this method will be reimplemented. - * + *

    * BEWARE, this method locks out the M_ORG_CLOSURE table, so org-related operations * would wait until it completes. - * + *

    * TODO this method is SQL service specific; it should be generalized/fixed somehow. */ void testOrgClosureConsistency(boolean repairIfNecessary, OperationResult testResult); 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 d63f0765517..0d5a7d7e53b 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 @@ -6,6 +6,26 @@ */ package com.evolveum.midpoint.repo.cache; +import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; + +import static com.evolveum.midpoint.repo.cache.RepositoryCache.PassReasonType.*; +import static com.evolveum.midpoint.schema.GetOperationOptions.*; +import static com.evolveum.midpoint.schema.SelectorOptions.findRootOptions; +import static com.evolveum.midpoint.schema.cache.CacheType.*; +import static com.evolveum.midpoint.schema.util.TraceUtil.isAtLeastMinimal; +import static com.evolveum.midpoint.schema.util.TraceUtil.isAtLeastNormal; + +import java.util.Objects; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.PreDestroy; + +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.CacheInvalidationContext; import com.evolveum.midpoint.prism.Containerable; import com.evolveum.midpoint.prism.PrismContext; @@ -31,35 +51,16 @@ 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 org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import javax.annotation.PreDestroy; -import java.util.Objects; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import static com.evolveum.midpoint.repo.cache.RepositoryCache.PassReasonType.*; -import static com.evolveum.midpoint.schema.GetOperationOptions.*; -import static com.evolveum.midpoint.schema.SelectorOptions.findRootOptions; -import static com.evolveum.midpoint.schema.cache.CacheType.*; -import static com.evolveum.midpoint.schema.util.TraceUtil.isAtLeastMinimal; -import static com.evolveum.midpoint.schema.util.TraceUtil.isAtLeastNormal; -import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; /** * Read-through write-through per-session repository cache. - * + *

    * TODO doc * TODO logging perf measurements * * @author Radovan Semancik - * */ -@Component(value="cacheRepositoryService") +@Component(value = "cacheRepositoryService") public class RepositoryCache implements RepositoryService, Cacheable { private static final Trace LOGGER = TraceManager.getTrace(RepositoryCache.class); @@ -73,7 +74,6 @@ public class RepositoryCache implements RepositoryService, Cacheable { private static final String SEARCH_OBJECTS = CLASS_NAME_WITH_DOT + "searchObjects"; private static final String SEARCH_CONTAINERS = CLASS_NAME_WITH_DOT + "searchContainers"; private static final String COUNT_CONTAINERS = CLASS_NAME_WITH_DOT + "countContainers"; - private static final String LIST_RESOURCE_OBJECT_SHADOWS = CLASS_NAME_WITH_DOT + "listResourceObjectShadows"; private static final String MODIFY_OBJECT = CLASS_NAME_WITH_DOT + "modifyObject"; private static final String COUNT_OBJECTS = CLASS_NAME_WITH_DOT + "countObjects"; private static final String GET_VERSION = CLASS_NAME_WITH_DOT + "getVersion"; @@ -1070,7 +1070,7 @@ public int countObjects(Class type, ObjectQuery query, @NotNull public ModifyObjectResult modifyObject(Class type, String oid, Collection modifications, - OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { + OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { return modifyObject(type, oid, modifications, null, parentResult); } @@ -1362,45 +1362,6 @@ private PassReason getPassReason(Collection return new PassReason(UNSUPPORTED_OPTION, cloned.toString()); } - @Override - @Deprecated - public PrismObject listAccountShadowOwner(String accountOid, OperationResult parentResult) - throws ObjectNotFoundException { - OperationResult result = parentResult.subresult(LIST_ACCOUNT_SHADOW_OWNER) - .addParam("accountOid", accountOid) - .build(); - Long startTime = repoOpStart(); - try { - return repositoryService.listAccountShadowOwner(accountOid, result); - } catch (Throwable t) { - result.recordFatalError(t); - throw t; - } finally { - repoOpEnd(startTime); - result.computeStatusIfUnknown(); - } - } - - @Override - public List> listResourceObjectShadows(String resourceOid, - Class resourceObjectShadowType, OperationResult parentResult) throws ObjectNotFoundException, - SchemaException { - OperationResult result = parentResult.subresult(LIST_RESOURCE_OBJECT_SHADOWS) - .addParam("resourceOid", resourceOid) - .addParam("resourceObjectShadowType", resourceObjectShadowType) - .build(); - Long startTime = repoOpStart(); - try { - return repositoryService.listResourceObjectShadows(resourceOid, resourceObjectShadowType, result); - } catch (Throwable t) { - result.recordFatalError(t); - throw t; - } finally { - repoOpEnd(startTime); - result.computeStatusIfUnknown(); - } - } - @Override public String getVersion(Class type, String oid, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { @@ -1754,7 +1715,6 @@ public void unregister() { cacheRegistry.unregisterCacheableService(this); } - @Override public ConflictWatcher createAndRegisterConflictWatcher(@NotNull String oid) { return repositoryService.createAndRegisterConflictWatcher(oid); @@ -1914,10 +1874,12 @@ enum PassReasonType { private static final class PassReason { private final PassReasonType type; private final String comment; + private PassReason(PassReasonType type) { this.type = type; this.comment = null; } + private PassReason(PassReasonType type, String comment) { this.type = type; this.comment = comment; @@ -1976,14 +1938,14 @@ public Object getObject() { public Collection getStateInformation() { List rv = new ArrayList<>(); rv.add(new SingleCacheStateInformationType(prismContext) - .name(LocalObjectCache.class.getName()) - .size(LocalObjectCache.getTotalSize(LOCAL_OBJECT_CACHE_INSTANCE))); + .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))); + .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))); + .name(LocalVersionCache.class.getName()) + .size(LocalVersionCache.getTotalSize(LOCAL_VERSION_CACHE_INSTANCE))); rv.addAll(globalObjectCache.getStateInformation()); rv.addAll(globalVersionCache.getStateInformation()); rv.addAll(globalQueryCache.getStateInformation()); diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/Source.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/Source.java index 0dd8c87a3fa..a8e668248f3 100644 --- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/Source.java +++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/Source.java @@ -10,6 +10,7 @@ import com.evolveum.midpoint.prism.Item; import com.evolveum.midpoint.prism.ItemDefinition; +import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.util.ItemDeltaItem; @@ -90,14 +91,14 @@ public String debugDump(int indent) { } @NotNull - public ItemDeltaItemType toItemDeltaItemType() throws SchemaException { + public ItemDeltaItemType toItemDeltaItemType(PrismContext prismContext) throws SchemaException { ItemDeltaItemType rv = new ItemDeltaItemType(); - rv.setOldItem(ItemType.fromItem(getItemOld())); + rv.setOldItem(ItemType.fromItem(getItemOld(), prismContext)); ItemDelta delta = getDelta(); if (delta != null) { rv.getDelta().addAll(DeltaConvertor.toItemDeltaTypes(delta)); } - rv.setNewItem(ItemType.fromItem(getItemNew())); + rv.setNewItem(ItemType.fromItem(getItemNew(), prismContext)); return rv; } } diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/ListAccountShadowOwnerTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/ListAccountShadowOwnerTest.java deleted file mode 100644 index e17a518a4a5..00000000000 --- a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/ListAccountShadowOwnerTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2010-2013 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ - -package com.evolveum.midpoint.repo.sql; - -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; - -import java.io.File; -import java.util.Collection; -import java.util.List; - -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.testng.AssertJUnit; -import org.testng.annotations.Test; - -import com.evolveum.midpoint.prism.Objectable; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismProperty; -import com.evolveum.midpoint.prism.delta.ItemDelta; -import com.evolveum.midpoint.prism.polystring.PolyString; -import com.evolveum.midpoint.prism.util.PrismTestUtil; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; - -/** - * @author lazyman - */ -@ContextConfiguration(locations = {"../../../../../ctx-test.xml"}) -@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) -public class ListAccountShadowOwnerTest extends BaseSQLRepoTest { - - @Test - public void listExistingOwner() throws Exception { - OperationResult result = new OperationResult("List owner"); - - //insert sample data - final File OBJECTS_FILE = new File(FOLDER_BASIC, "objects.xml"); - List> elements = prismContext.parserFor(OBJECTS_FILE).parseObjects(); - for (PrismObject object : elements) { - repositoryService.addObject(object, null, result); - } - - //look for account owner - PrismObject user = repositoryService.listAccountShadowOwner("1234", result); - - assertNotNull("No owner for account 1234", user); - PrismProperty name = user.findProperty(ObjectType.F_NAME); - AssertJUnit.assertNotNull(name); - AssertJUnit.assertEquals("atestuserX00003", ((PolyString) name.getRealValue()).getOrig()); - } - - @Test - public void listNonExistingOwner() throws Exception { - OperationResult result = new OperationResult("LIST OWNER"); - - PrismObject user = repositoryService.listAccountShadowOwner("12345", result); - AssertJUnit.assertNull(user); - } - - @Test - public void testLinkUnlink() throws Exception { - // GIVEN - OperationResult result = new OperationResult("testLinkUnlink"); - PrismObject user = PrismTestUtil.parseObject(new File(FOLDER_BASIC, "user.xml")); - String userOid = repositoryService.addObject(user, null, result); - assertNotNull("User oid is null", userOid); - AssertJUnit.assertEquals("user oid is not equal to returned value", userOid, user.getOid()); - PrismObject account = PrismTestUtil.parseObject(new File(FOLDER_BASIC, "account-shadow.xml")); - String accountOid = repositoryService.addObject(account, null, result); - assertNotNull("Account oid is null, couldn't add account or what?", account); - AssertJUnit.assertEquals("account oid is not equal to returned value", accountOid, account.getOid()); - // precondition - PrismObject accountOwnerOid = repositoryService.listAccountShadowOwner(accountOid, result); - assertNull("Account has owner and should not have (precondition)", accountOwnerOid); - - // WHEN (link account) - Collection modifications = prismContext.deltaFactory().reference().createModificationAddCollection(UserType.class, - UserType.F_LINK_REF, account); - repositoryService.modifyObject(UserType.class, userOid, modifications, result); - // THEN - accountOwnerOid = repositoryService.listAccountShadowOwner(accountOid, result); - assertEquals("listAccountShadowOwner returned wrong value", userOid, accountOwnerOid); - - // WHEN (unlink account) - modifications = prismContext.deltaFactory().reference().createModificationDeleteCollection(UserType.class, UserType.F_LINK_REF, - account); - repositoryService.modifyObject(UserType.class, userOid, modifications, result); - // THEN - accountOwnerOid = repositoryService.listAccountShadowOwner(accountOid, result); - assertNull("listAccountShadowOwner returned non-null value after unlink", accountOwnerOid); - } - - private void assertEquals(String string, String userOid, PrismObject accountOwner) { - AssertJUnit.assertEquals(string, userOid, accountOwner != null ? accountOwner.getOid() : null); - } -} diff --git a/repo/repo-sql-impl-test/testng-integration.xml b/repo/repo-sql-impl-test/testng-integration.xml index 0384c9c4fde..4e0c2af25a1 100644 --- a/repo/repo-sql-impl-test/testng-integration.xml +++ b/repo/repo-sql-impl-test/testng-integration.xml @@ -29,7 +29,6 @@ - diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java index e1ff5ae17a9..e89e2930a17 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java @@ -13,25 +13,12 @@ import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; +import java.util.*; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; - import javax.xml.namespace.QName; -import com.evolveum.midpoint.repo.api.*; -import com.evolveum.midpoint.repo.sql.perf.SqlPerformanceMonitorImpl; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.lang.Validate; import org.hibernate.Session; import org.hibernate.SessionFactory; @@ -42,42 +29,20 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; -import com.evolveum.midpoint.common.configuration.api.MidpointConfiguration; import com.evolveum.midpoint.common.crypto.CryptoUtil; -import com.evolveum.midpoint.prism.ConsistencyCheckScope; -import com.evolveum.midpoint.prism.Containerable; -import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectDefinition; -import com.evolveum.midpoint.prism.PrismProperty; -import com.evolveum.midpoint.prism.PrismPropertyValue; +import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ItemDeltaCollectionsUtil; import com.evolveum.midpoint.prism.delta.PropertyDelta; import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; import com.evolveum.midpoint.prism.polystring.PolyString; -import com.evolveum.midpoint.prism.query.AllFilter; -import com.evolveum.midpoint.prism.query.NoneFilter; -import com.evolveum.midpoint.prism.query.ObjectFilter; -import com.evolveum.midpoint.prism.query.ObjectPaging; -import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.prism.query.*; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.repo.api.*; import com.evolveum.midpoint.repo.api.query.ObjectFilterExpressionEvaluator; -import com.evolveum.midpoint.repo.sql.helpers.BaseHelper; -import com.evolveum.midpoint.repo.sql.helpers.ObjectRetriever; -import com.evolveum.midpoint.repo.sql.helpers.ObjectUpdater; -import com.evolveum.midpoint.repo.sql.helpers.OrgClosureManager; -import com.evolveum.midpoint.repo.sql.helpers.SequenceHelper; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.LabeledString; -import com.evolveum.midpoint.schema.RelationRegistry; -import com.evolveum.midpoint.schema.RepositoryDiag; -import com.evolveum.midpoint.schema.RepositoryQueryDiagRequest; -import com.evolveum.midpoint.schema.RepositoryQueryDiagResponse; -import com.evolveum.midpoint.schema.ResultHandler; -import com.evolveum.midpoint.schema.SearchResultList; -import com.evolveum.midpoint.schema.SearchResultMetadata; -import com.evolveum.midpoint.schema.SelectorOptions; +import com.evolveum.midpoint.repo.sql.helpers.*; +import com.evolveum.midpoint.repo.sql.perf.SqlPerformanceMonitorImpl; +import com.evolveum.midpoint.schema.*; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.internals.InternalMonitor; import com.evolveum.midpoint.schema.internals.InternalsConfig; @@ -88,22 +53,17 @@ import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.PrettyPrinter; -import com.evolveum.midpoint.util.exception.CommunicationException; -import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.LoggingUtils; 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; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; /** * @author lazyman - * + *

    * Note: don't autowire this class - because of Spring AOP use it couldn't be found by implementation class; only by its interface. */ @Repository @@ -120,7 +80,6 @@ public class SqlRepositoryServiceImpl extends SqlBaseService implements Reposito private static final int RESTART_LIMIT = 1000; private static final Trace LOGGER = TraceManager.getTrace(SqlRepositoryServiceImpl.class); - private static final Trace LOGGER_PERFORMANCE = TraceManager.getTrace(PERFORMANCE_LOG_NAME); private static final int MAX_CONFLICT_WATCHERS = 10; // just a safeguard (watchers per thread should be at most 1-2) public static final int MAX_CONSTRAINT_NAME_LENGTH = 40; @@ -139,7 +98,6 @@ public class SqlRepositoryServiceImpl extends SqlBaseService implements Reposito @Autowired private OrgClosureManager closureManager; @Autowired private BaseHelper baseHelper; @Autowired private MatchingRuleRegistry matchingRuleRegistry; - @Autowired private MidpointConfiguration midpointConfiguration; @Autowired private PrismContext prismContext; @Autowired private RelationRegistry relationRegistry; @Autowired private SystemConfigurationChangeDispatcher systemConfigurationChangeDispatcher; @@ -219,8 +177,9 @@ private RV executeAttempts(String oid, String operationName, Class type, } } - private RV executeAttemptsNoSchemaException(String oid, String operationName, Class type, String operationVerb, OperationResult subResult, - ResultSupplier supplier) throws ObjectNotFoundException { + private RV executeAttemptsNoSchemaException( + String oid, String operationName, Class type, String operationVerb, + OperationResult subResult, ResultSupplier supplier) throws ObjectNotFoundException { try { return executeAttempts(oid, operationName, type, operationVerb, subResult, supplier); } catch (SchemaException e) { @@ -228,7 +187,8 @@ private RV executeAttemptsNoSchemaException(String oid, String operationNam } } - private RV executeQueryAttemptsNoSchemaException(ObjectQuery query, String operationName, Class type, String operationVerb, OperationResult subResult, + private RV executeQueryAttemptsNoSchemaException(ObjectQuery query, + String operationName, Class type, String operationVerb, OperationResult subResult, Supplier emptyQueryResultSupplier, ResultQueryBasedSupplier supplier) { try { return executeQueryAttempts(query, operationName, type, operationVerb, subResult, emptyQueryResultSupplier, supplier); @@ -279,29 +239,7 @@ public PrismObject searchShadowOwner(String shadowOid, return executeAttempts(shadowOid, OP_SEARCH_SHADOW_OWNER, FocusType.class, "searching shadow owner", subResult, () -> objectRetriever.searchShadowOwnerAttempt(shadowOid, options, subResult) ); - } catch (ObjectNotFoundException|SchemaException e) { - throw new AssertionError("Should not occur; exception should have been treated in searchShadowOwnerAttempt.", e); - } - } - - @Override - @Deprecated - public PrismObject listAccountShadowOwner(String accountOid, OperationResult result) - throws ObjectNotFoundException { - Validate.notEmpty(accountOid, "Oid must not be null or empty."); - Validate.notNull(result, "Operation result must not be null."); - - LOGGER.debug("Selecting account shadow owner for account {}.", new Object[]{accountOid}); - - OperationResult subResult = result.subresult(LIST_ACCOUNT_SHADOW) - .addParam("accountOid", accountOid) - .build(); - - try { - return executeAttempts(accountOid, OP_LIST_ACCOUNT_SHADOW_OWNER, UserType.class, "listing account shadow owner", - subResult, () -> objectRetriever.listAccountShadowOwnerAttempt(accountOid, subResult) - ); - } catch (ObjectNotFoundException|SchemaException e) { + } catch (ObjectNotFoundException | SchemaException e) { throw new AssertionError("Should not occur; exception should have been treated in searchShadowOwnerAttempt.", e); } } @@ -363,7 +301,7 @@ public SearchResultList searchContainers(Class t .build(); return executeQueryAttempts(query, "searchContainers", type, "searching", result, - () -> new SearchResultList<>(new ArrayList(0)), + () -> new SearchResultList<>(new ArrayList<>(0)), (q) -> objectRetriever.searchContainersAttempt(type, q, options, result)); } @@ -525,7 +463,7 @@ public int countObjects(Class type, ObjectQuery query, } public int countObjects(Class type, ObjectQuery query, - Collection> options, OperationResult result) { + Collection> options, OperationResult result) { Validate.notNull(type, "Object type must not be null."); Validate.notNull(result, "Operation result must not be null."); @@ -654,41 +592,6 @@ public ModifyObjectResult modifyObject(Class type, } } - @Override - public List> listResourceObjectShadows(String resourceOid, - Class resourceObjectShadowType, OperationResult result) throws ObjectNotFoundException, SchemaException { - Validate.notEmpty(resourceOid, "Resource oid must not be null or empty."); - Validate.notNull(resourceObjectShadowType, "Resource object shadow type must not be null."); - Validate.notNull(result, "Operation result must not be null."); - - LOGGER.debug("Listing resource object shadows '{}' for resource '{}'.", - new Object[]{resourceObjectShadowType.getSimpleName(), resourceOid}); - OperationResult subResult = result.subresult(LIST_RESOURCE_OBJECT_SHADOWS) - .addParam("oid", resourceOid) - .addParam("resourceObjectShadowType", resourceObjectShadowType) - .build(); - - final String operation = "listing resource object shadows"; - int attempt = 1; - - SqlPerformanceMonitorImpl pm = getPerformanceMonitor(); - long opHandle = pm.registerOperationStart(OP_LIST_RESOURCE_OBJECT_SHADOWS, ShadowType.class); - - // TODO executeAttempts - try { - while (true) { - try { - return objectRetriever.listResourceObjectShadowsAttempt(resourceOid, resourceObjectShadowType, subResult); - } catch (RuntimeException ex) { - attempt = baseHelper.logOperationAttempt(resourceOid, operation, attempt, ex, subResult); - pm.registerOperationNewAttempt(opHandle, attempt); - } - } - } finally { - pm.registerOperationFinish(opHandle, attempt); - } - } - @Override public RepositoryDiag getRepositoryDiag() { LOGGER.debug("Getting repository diagnostics."); @@ -740,7 +643,6 @@ public void execute(Connection connection) throws SQLException { details.add(new LabeledString(DETAILS_TRANSACTION_ISOLATION, getTransactionIsolation(connection, config))); - Properties info = connection.getClientInfo(); if (info == null) { return; @@ -760,9 +662,9 @@ public void execute(Connection connection) throws SQLException { SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) sessionFactory; // we try to override configuration which was read from sql repo configuration with // real configuration from session factory - if(sessionFactoryImpl.getDialect() != null) { - for(int i =0; i String getVersion(Class type, String oid, Opera Validate.notNull(oid, "Object oid must not be null."); Validate.notNull(parentResult, "Operation result must not be null."); - LOGGER.debug("Getting version for {} with oid '{}'.", new Object[]{type.getSimpleName(), oid}); + LOGGER.debug("Getting version for {} with oid '{}'.", new Object[] { type.getSimpleName(), oid }); OperationResult subResult = parentResult.subresult(GET_VERSION) .addQualifier(type.getSimpleName()) @@ -932,11 +834,20 @@ public SearchResultMetadata searchObjectsIterative(Class< SearchResultMetadata rv = null; // todo what about returning values from other search methods? switch (iterationMethod) { - case SINGLE_TRANSACTION: rv = searchObjectsIterativeBySingleTransaction(type, query, handler, options, subResult); break; - case SIMPLE_PAGING: objectRetriever.searchObjectsIterativeByPaging(type, query, handler, options, subResult); break; - case STRICTLY_SEQUENTIAL_PAGING: objectRetriever.searchObjectsIterativeByPagingStrictlySequential(type, query, handler, options, subResult); break; - case FETCH_ALL: objectRetriever.searchObjectsIterativeByFetchAll(type, query, handler, options, subResult); break; - default: throw new AssertionError("iterationMethod: " + iterationMethod); + case SINGLE_TRANSACTION: + rv = searchObjectsIterativeBySingleTransaction(type, query, handler, options, subResult); + break; + case SIMPLE_PAGING: + objectRetriever.searchObjectsIterativeByPaging(type, query, handler, options, subResult); + break; + case STRICTLY_SEQUENTIAL_PAGING: + objectRetriever.searchObjectsIterativeByPagingStrictlySequential(type, query, handler, options, subResult); + break; + case FETCH_ALL: + objectRetriever.searchObjectsIterativeByFetchAll(type, query, handler, options, subResult); + break; + default: + throw new AssertionError("iterationMethod: " + iterationMethod); } return rv; } @@ -991,12 +902,13 @@ private SearchResultMetadata searchObjectsIterativeBySing } @Override - public boolean isAnySubordinate(String upperOrgOid, Collection lowerObjectOids) throws SchemaException { + public boolean isAnySubordinate(String upperOrgOid, Collection lowerObjectOids) { Validate.notNull(upperOrgOid, "upperOrgOid must not be null."); Validate.notNull(lowerObjectOids, "lowerObjectOids must not be null."); - if (LOGGER.isTraceEnabled()) - LOGGER.trace("Querying for subordination upper {}, lower {}", new Object[]{upperOrgOid, lowerObjectOids}); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Querying for subordination upper {}, lower {}", new Object[] { upperOrgOid, lowerObjectOids }); + } if (lowerObjectOids.isEmpty()) { // trivial case @@ -1022,7 +934,6 @@ public boolean isAnySubordinate(String upperOrgOid, Collection lowerObje } } - @Override public long advanceSequence(String oid, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { @@ -1034,8 +945,7 @@ public long advanceSequence(String oid, OperationResult parentResult) throws Obj .addParam("oid", oid) .build(); - if (LOGGER.isTraceEnabled()) - LOGGER.trace("Advancing sequence {}", oid); + if (LOGGER.isTraceEnabled()) { LOGGER.trace("Advancing sequence {}", oid); } // TODO executeAttempts int attempt = 1; @@ -1056,7 +966,6 @@ public long advanceSequence(String oid, OperationResult parentResult) throws Obj } } - @Override public void returnUnusedValuesToSequence(String oid, Collection unusedValues, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { @@ -1194,17 +1103,17 @@ public boolean selectorMatches(ObjectSelectorType objectS } @Override - public boolean isDescendant(PrismObject object, String orgOid) throws SchemaException { + public boolean isDescendant(PrismObject object, String orgOid) { List objParentOrgRefs = object.asObjectable().getParentOrgRef(); List objParentOrgOids = new ArrayList<>(objParentOrgRefs.size()); - for (ObjectReferenceType objParentOrgRef: objParentOrgRefs) { + for (ObjectReferenceType objParentOrgRef : objParentOrgRefs) { objParentOrgOids.add(objParentOrgRef.getOid()); } return isAnySubordinate(orgOid, objParentOrgOids); } @Override - public boolean isAncestor(PrismObject object, String oid) throws SchemaException { + public boolean isAncestor(PrismObject object, String oid) { if (object.getOid() == null) { return false; } @@ -1319,7 +1228,8 @@ public void addDiagnosticInformation(Class type, Strin // TODO replace by something in system configuration (postponing until this feature is used more) - private static final Map DIAG_INFO_CLEANUP_POLICY = new HashMap<>(); + private static final Map DIAG_INFO_CLEANUP_POLICY = new HashMap<>(); + static { DIAG_INFO_CLEANUP_POLICY.put(SchemaConstants.TASK_THREAD_DUMP_URI, 5); DIAG_INFO_CLEANUP_POLICY.put(null, 2); @@ -1344,7 +1254,7 @@ private boolean pruneDiagnosticInformation(Class type, List oldToPrune = oldInformationList.stream() .filter(i -> infoType.equals(i.getType())) .collect(Collectors.toList()); - int pruneToSize = limit > 0 ? limit-1 : 0; + int pruneToSize = limit > 0 ? limit - 1 : 0; if (oldToPrune.size() > pruneToSize) { oldToPrune.sort(Comparator.nullsFirst(Comparator.comparing(i -> XmlTypeConverter.toDate(i.getTimestamp())))); List toDelete = oldToPrune.subList(0, oldToPrune.size() - pruneToSize); diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java index 63355be0884..75ab55fdec2 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java @@ -7,6 +7,23 @@ package com.evolveum.midpoint.repo.sql.helpers; +import static org.apache.commons.lang3.ArrayUtils.getLength; +import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; + +import java.util.*; +import java.util.stream.Collectors; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.xml.namespace.QName; + +import org.hibernate.*; +import org.hibernate.query.NativeQuery; +import org.hibernate.query.Query; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.common.crypto.CryptoUtil; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.path.ItemName; @@ -42,22 +59,6 @@ import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; -import org.hibernate.*; -import org.hibernate.query.NativeQuery; -import org.hibernate.query.Query; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.xml.namespace.QName; -import java.util.*; -import java.util.stream.Collectors; - -import static org.apache.commons.lang3.ArrayUtils.getLength; -import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; /** * @author lazyman, mederly @@ -66,7 +67,6 @@ public class ObjectRetriever { public static final String CLASS_DOT = ObjectRetriever.class.getName() + "."; - public static final String OPERATION_GET_OBJECT_INTERNAL = CLASS_DOT + "getObjectInternal"; private static final Trace LOGGER = TraceManager.getTrace(ObjectRetriever.class); private static final Trace LOGGER_PERFORMANCE = TraceManager.getTrace(SqlRepositoryServiceImpl.PERFORMANCE_LOG_NAME); @@ -232,7 +232,7 @@ public PrismObject searchShadowOwnerAttempt(String shad query.setParameter("oid", shadowOid); query.setResultTransformer(GetObjectResult.RESULT_STYLE.getResultTransformer()); - @SuppressWarnings({"unchecked", "raw"}) + @SuppressWarnings({ "unchecked", "raw" }) List focuses = query.list(); LOGGER.trace("Found {} focuses, transforming data to JAXB types.", focuses != null ? focuses.size() : 0); @@ -257,8 +257,7 @@ public PrismObject searchShadowOwnerAttempt(String shad return owner; } - public PrismObject listAccountShadowOwnerAttempt(String accountOid, OperationResult result) - throws ObjectNotFoundException { + public PrismObject listAccountShadowOwnerAttempt(String accountOid, OperationResult result) { LOGGER_PERFORMANCE.debug("> list account shadow owner oid={}", accountOid); PrismObject userType = null; Session session = null; @@ -268,7 +267,7 @@ public PrismObject listAccountShadowOwnerAttempt(String accountOid, Op query.setParameter("oid", accountOid); query.setResultTransformer(GetObjectResult.RESULT_STYLE.getResultTransformer()); - @SuppressWarnings({"unchecked", "raw"}) + @SuppressWarnings({ "unchecked", "raw" }) List users = query.list(); LOGGER.trace("Found {} users, transforming data to JAXB types.", users != null ? users.size() : 0); @@ -333,8 +332,8 @@ public int countObjectsAttempt(Class type, ObjectQuery return count; } - //TODO copied from QueryInterpreter, remove if full support for searching AssignmentHolderType is implemented MID-5579 + /** * Both ObjectType and AssignmentHolderType are mapped to RObject. So when searching for AssignmentHolderType it is not sufficient to * query this table. This method hacks this situation a bit by introducing explicit type filter for AssignmentHolderType. @@ -391,7 +390,7 @@ public SearchResultList> searchObjectsAtte QueryEngine2 engine = new QueryEngine2(getConfiguration(), extItemDictionary, prismContext, relationRegistry); rQuery = engine.interpret(query, type, options, false, session); - @SuppressWarnings({"unchecked", "raw"}) + @SuppressWarnings({ "unchecked", "raw" }) List queryResult = rQuery.list(); LOGGER.trace("Found {} objects, translating to JAXB.", queryResult != null ? queryResult.size() : 0); @@ -456,21 +455,21 @@ public SearchResultList searchContainersAttempt(Cla RQuery rQuery = engine.interpret(query, type, options, false, session); if (cases) { - @SuppressWarnings({"unchecked", "raw"}) + @SuppressWarnings({ "unchecked", "raw" }) List items = rQuery.list(); LOGGER.trace("Found {} items (cases), translating to JAXB.", items.size()); - Map> campaignsCache = new HashMap<>(); + Map> campaignsCache = new HashMap<>(); for (GetContainerableResult item : items) { @SuppressWarnings({ "raw", "unchecked" }) C value = (C) caseHelper.updateLoadedCertificationCase(item, campaignsCache, options, session, result); list.add(value); } } else if (workItems) { - @SuppressWarnings({"unchecked", "raw"}) + @SuppressWarnings({ "unchecked", "raw" }) List items = rQuery.list(); LOGGER.trace("Found {} work items, translating to JAXB.", items.size()); - Map> casesCache = new HashMap<>(); - Map> campaignsCache = new HashMap<>(); + Map> casesCache = new HashMap<>(); + Map> campaignsCache = new HashMap<>(); for (GetCertificationWorkItemResult item : items) { //LOGGER.trace("- {}", item); @SuppressWarnings({ "raw", "unchecked" }) @@ -479,17 +478,17 @@ public SearchResultList searchContainersAttempt(Cla } } else { assert caseWorkItems; - @SuppressWarnings({"unchecked", "raw"}) + @SuppressWarnings({ "unchecked", "raw" }) List items = rQuery.list(); LOGGER.trace("Found {} items (case work items), translating to JAXB.", items.size()); - Map> casesCache = new HashMap<>(); + Map> casesCache = new HashMap<>(); for (GetContainerableIdOnlyResult item : items) { try { @SuppressWarnings({ "raw", "unchecked" }) C value = (C) caseManagementHelper.updateLoadedCaseWorkItem(item, casesCache, options, session, result); list.add(value); - } catch (ObjectNotFoundException|DtoTranslationException e) { + } catch (ObjectNotFoundException | DtoTranslationException e) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't retrieve case work item for {}", e, item); } } @@ -745,7 +744,7 @@ private Collection> getIndexOnlyExtensi } private void applyShadowAttributeDefinitions(Class anyValueType, - PrismObject object, Session session) throws SchemaException { + PrismObject object, Session session) throws SchemaException { PrismContainer attributes = object.findContainer(ShadowType.F_ATTRIBUTES); @@ -753,7 +752,7 @@ private void applyShadowAttributeDefinitions(Class anyValue query.setParameter("oid", object.getOid()); query.setParameter("ownerType", RObjectExtensionType.ATTRIBUTES); - @SuppressWarnings({"unchecked", "raw"}) + @SuppressWarnings({ "unchecked", "raw" }) List identifiers = query.list(); if (identifiers == null || identifiers.isEmpty()) { return; @@ -785,7 +784,7 @@ private MutableItemDefinition createDynamicDefinition(RExtItem extItem, ItemN RItemKind rValType = extItem.getKind(); MutableItemDefinition def; if (rValType == RItemKind.PROPERTY) { - def = prismContext.definitionFactory().createPropertyDefinition(name, type); + def = prismContext.definitionFactory().createPropertyDefinition(name, type); } else if (rValType == RItemKind.REFERENCE) { def = prismContext.definitionFactory().createReferenceDefinition(name, type); } else { @@ -797,40 +796,6 @@ private MutableItemDefinition createDynamicDefinition(RExtItem extItem, ItemN return def; } - public List> listResourceObjectShadowsAttempt( - String resourceOid, Class resourceObjectShadowType, OperationResult result) - throws ObjectNotFoundException, SchemaException { - - LOGGER_PERFORMANCE.debug("> list resource object shadows {}, for resource oid={}", - resourceObjectShadowType.getSimpleName(), resourceOid); - List> list = new ArrayList<>(); - Session session = null; - try { - session = baseHelper.beginReadOnlyTransaction(); - Query query = session.getNamedQuery("listResourceObjectShadows"); - query.setParameter("oid", resourceOid); - query.setResultTransformer(GetObjectResult.RESULT_STYLE.getResultTransformer()); - - @SuppressWarnings({"unchecked", "raw"}) - List shadows = query.list(); - LOGGER.debug("Query returned {} shadows, transforming to JAXB types.", shadows != null ? shadows.size() : 0); - - if (shadows != null) { - for (GetObjectResult shadow : shadows) { - PrismObject prismObject = updateLoadedObject(shadow, resourceObjectShadowType, null, null, null, session, result); - list.add(prismObject); - } - } - session.getTransaction().commit(); - } catch (SchemaException | RuntimeException ex) { - baseHelper.handleGeneralException(ex, session, result); - } finally { - baseHelper.cleanupSessionAndResult(session, result); - } - - return list; - } - private void validateObjectType(PrismObject prismObject, Class type) throws SchemaException { if (prismObject == null || !type.isAssignableFrom(prismObject.getCompileTimeClass())) { @@ -847,7 +812,7 @@ private void validateObjectType(PrismObject prismObjec } public String getVersionAttempt(Class type, String oid, OperationResult result) - throws ObjectNotFoundException, SchemaException { + throws ObjectNotFoundException { LOGGER_PERFORMANCE.debug("> get version {}, oid={}", type.getSimpleName(), oid); String version = null; @@ -874,8 +839,7 @@ public String getVersionAttempt(Class type, String oid } public void searchObjectsIterativeAttempt(Class type, ObjectQuery query, ResultHandler handler, - Collection> options, OperationResult result, Set retrievedOids) - throws SchemaException { + Collection> options, OperationResult result, Set retrievedOids) { Set newlyRetrievedOids = new HashSet<>(); Session session = null; try { @@ -945,7 +909,8 @@ public void searchObjectsIterativeByPaging(Class type, remaining = paging.getMaxSize() != null ? paging.getMaxSize() : repositoryService.countObjects(type, query, options, result) - offset; } -main: while (remaining > 0) { + main: + while (remaining > 0) { paging.setOffset(offset); paging.setMaxSize(remaining < batchSize ? remaining : batchSize); @@ -974,23 +939,23 @@ public void searchObjectsIterativeByPaging(Class type, /** * Strictly-sequential version of paged search. - * + *

    * Assumptions: - * - During processing of returned object(s), any objects can be added, deleted or modified. - * + * - During processing of returned object(s), any objects can be added, deleted or modified. + *

    * Guarantees: - * - We return each object that existed in the moment of search start: - * - exactly once if it was not deleted in the meanwhile, - * - at most once otherwise. - * - However, we may or may not return any objects that were added during the processing. - * + * - We return each object that existed in the moment of search start: + * - exactly once if it was not deleted in the meanwhile, + * - at most once otherwise. + * - However, we may or may not return any objects that were added during the processing. + *

    * Constraints: - * - There can be no ordering prescribed. We use our own ordering. - * - We also disallow any explicit paging - except for maxSize setting. - * - * Implementation is very simple - we fetch objects ordered by OID, and remember last OID fetched. - * Obviously no object will be present in output more than once. - * Objects that are not deleted will be there exactly once, provided their oid is not changed. + * - There can be no ordering prescribed. We use our own ordering. + * - We also disallow any explicit paging - except for maxSize setting. + *

    + * Implementation is very simple - we fetch objects ordered by OID, and remember last OID fetched. + * Obviously no object will be present in output more than once. + * Objects that are not deleted will be there exactly once, provided their oid is not changed. */ public void searchObjectsIterativeByPagingStrictlySequential( Class type, ObjectQuery query, ResultHandler handler, @@ -1017,7 +982,8 @@ public void searchObjectsIterativeByPagingStrictlySequent ObjectPaging paging = prismContext.queryFactory().createPaging(); pagedQuery.setPaging(paging); -main: for (;;) { + main: + for (; ; ) { paging.setCookie(lastOid != null ? lastOid : NULL_OID_MARKER); paging.setMaxSize(Math.min(batchSize, defaultIfNull(maxSize, Integer.MAX_VALUE))); @@ -1145,7 +1111,7 @@ void attachDiagDataIfRequested(PrismValue value, byte[] fullObject, Collection item, byte[] fullObject, Collection> options) { + private void attachDiagDataIfRequested(Item item, byte[] fullObject, Collection> options) { if (GetOperationOptions.isAttachDiagData(SelectorOptions.findRootOptions(options))) { item.setUserData(RepositoryService.KEY_DIAG_DATA, new RepositoryObjectDiagnosticData(getLength(fullObject))); } diff --git a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/UsersTest.java b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/UsersTest.java index be97aa19a03..f164294eb5a 100644 --- a/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/UsersTest.java +++ b/testing/schrodingertest/src/test/java/com/evolveum/midpoint/testing/schrodinger/UsersTest.java @@ -9,12 +9,23 @@ import com.codeborne.selenide.Selenide; +import com.evolveum.midpoint.schrodinger.component.assignmentholder.AssignmentHolderObjectListTable; import com.evolveum.midpoint.schrodinger.component.common.Paging; +import com.evolveum.midpoint.schrodinger.component.common.Popover; +import com.evolveum.midpoint.schrodinger.component.common.Search; import com.evolveum.midpoint.schrodinger.page.user.ListUsersPage; import com.evolveum.midpoint.schrodinger.page.user.UserPage; +import com.evolveum.midpoint.testing.schrodinger.scenarios.ScenariosCommons; + +import org.openqa.selenium.Keys; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import java.io.File; +import java.io.IOException; + import static com.codeborne.selenide.Selenide.screenshot; /** @@ -22,6 +33,20 @@ */ public class UsersTest extends AbstractSchrodingerTest { + private static final File LOOKUP_TABLE_SUBTYPES = new File("src/test/resources/configuration/objects/lookuptable/subtypes.xml"); + private static final File OT_FOR_LOOKUP_TABLE_SUBTYPES = new File("src/test/resources/configuration/objects/objecttemplate/object-template-for-lookup-table-subtypes.xml"); + private static final File SYSTEM_CONFIG_WITH_LOOKUP_TABLE = new File("src/test/resources/configuration/objects/systemconfig/system-configuration-with-lookup-table.xml"); + + @BeforeClass + @Override + public void beforeClass() throws IOException { + super.beforeClass(); + importObject(LOOKUP_TABLE_SUBTYPES, true); + importObject(OT_FOR_LOOKUP_TABLE_SUBTYPES, true); + importObject(SYSTEM_CONFIG_WITH_LOOKUP_TABLE, true); + + } + @Test public void testUserTablePaging() { ListUsersPage users = basicPage.listUsers(); @@ -52,6 +77,45 @@ public void testUserTablePaging() { paging.actualPageMinusOne(); } + @Test + public void testSearchWithLookupTable() { + + UserPage user = basicPage.newUser(); + user.selectTabBasic() + .form() + .addAttributeValue("name", "searchUser") + .addAttributeValue("subtype", "Extern") + .and() + .and() + .clickSave(); + + + ListUsersPage users = basicPage.listUsers(); + + Assert.assertTrue( + users + .table() + .search() + .byItem("subtype") + .inputValueWithEnter("Extern") + .and() + .and() + .currentTableContains("searchUser") + ); + + Assert.assertFalse( + users + .table() + .search() + .byItem("subtype") + .inputValueWithEnter("Employee") + .and() + .and() + .currentTableContains("searchUser") + ); + + } + private void addUser(String name) { UserPage user = basicPage.newUser(); user.selectTabBasic() diff --git a/testing/schrodingertest/src/test/resources/configuration/objects/lookuptable/subtypes.xml b/testing/schrodingertest/src/test/resources/configuration/objects/lookuptable/subtypes.xml new file mode 100644 index 00000000000..414ca8ecae8 --- /dev/null +++ b/testing/schrodingertest/src/test/resources/configuration/objects/lookuptable/subtypes.xml @@ -0,0 +1,18 @@ + + + + Subtypes + + EMP + + + + EXT + + + diff --git a/testing/schrodingertest/src/test/resources/configuration/objects/objecttemplate/object-template-for-lookup-table-subtypes.xml b/testing/schrodingertest/src/test/resources/configuration/objects/objecttemplate/object-template-for-lookup-table-subtypes.xml new file mode 100644 index 00000000000..5e8e5940de1 --- /dev/null +++ b/testing/schrodingertest/src/test/resources/configuration/objects/objecttemplate/object-template-for-lookup-table-subtypes.xml @@ -0,0 +1,15 @@ + + + + lookup table item subtypes + + subtype + subtype + + + diff --git a/testing/schrodingertest/src/test/resources/configuration/objects/systemconfig/system-configuration-with-lookup-table.xml b/testing/schrodingertest/src/test/resources/configuration/objects/systemconfig/system-configuration-with-lookup-table.xml new file mode 100644 index 00000000000..76a458a37ef --- /dev/null +++ b/testing/schrodingertest/src/test/resources/configuration/objects/systemconfig/system-configuration-with-lookup-table.xml @@ -0,0 +1,366 @@ + + + + SystemConfiguration + + + + + + ERROR + ro.isdc.wro.extensions.processor.css.Less4jProcessor + + + OFF + org.hibernate.engine.jdbc.spi.SqlExceptionHelper + + + OFF + org.hibernate.engine.jdbc.batch.internal.BatchingBatch + + + WARN + org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl + + + OFF + org.hibernate.internal.ExceptionMapperStandardImpl + + + OFF + net.sf.jasperreports.engine.fill.JRFillDataset + + + WARN + org.apache.wicket.resource.PropertiesFactory + + + ERROR + org.springframework.context.support.ResourceBundleMessageSource + + + INFO + com.evolveum.midpoint.model.impl.lens.projector.Projector + + + INFO + com.evolveum.midpoint.model.impl.lens.Clockwork + + + %date [%X{subsystem}] [%thread] %level \(%logger\): %msg%n + MIDPOINT_LOG + ${midpoint.home}/log/midpoint.log + ${midpoint.home}/log/midpoint-%d{yyyy-MM-dd}.%i.log + 10 + 100MB + true + + + %date %level: %msg%n + MIDPOINT_PROFILE_LOG + ${midpoint.home}/log/midpoint-profile.log + ${midpoint.home}/log/midpoint-profile-%d{yyyy-MM-dd}.%i.log + 10 + 100MB + true + + MIDPOINT_LOG + INFO + + false +

    false
    + + + + + P3M + + + P1M + + + + + + performance + Performance tracing + true + true + performance-trace %{timestamp} %{focusName} %{milliseconds} + true + true + + + functional + Functional tracing + true + functional-trace %{timestamp} %{focusName} + true + true + true + + normal + + + + functional-model-logging + Functional tracing (with model logging) + true + functional-trace %{timestamp} %{focusName} + true + true + true + + + com.evolveum.midpoint.model + TRACE + + + + normal + + + + functional-sql-logging + Functional tracing (with SQL logging) + true + functional-trace %{timestamp} %{focusName} + true + true + true + + + org.hibernate.SQL + TRACE + + + + normal + + + + + + + /self/profile + + View/edit your profile + + fa fa-user + + green + http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#selfProfile + http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#selfAll + + + /self/credentials + + View/edit your credentials + + fa fa-shield + + blue + http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#selfCredentials + http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#selfAll + + + /admin/users + + + fa fa-users + + red + http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#users + + + /admin/resources + + + fa fa-database + + purple + http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#resources + + + + + safe + + "Safe" expression profile. It is supposed to contain only operations that are "safe", + i.e. operations that have very little risk to harm the system, circumvent midPoint security + and so on. Use of those operations should be reasonably safe in all expressions. + However, there are limitations. This profile may incomplete or it may even be not completely secure. + Proper security testing of this profile was not yet conducted. It is provided here "AS IS", + without any guarantees. Use at your own risk. + + deny + + asIs + allow + + + path + allow + + + value + allow + + + const + allow + + + script + deny + + + + + script-safe + deny + + com.evolveum.midpoint.xml.ns._public.common.common_3 + MidPoint common schema - generated bean classes + allow + + + com.evolveum.prism.xml.ns._public.types_3 + Prism schema - bean classes + allow + + + java.lang.Integer + allow + + + java.lang.Object + Basic Java operations. + deny + + equals + allow + + + hashCode + allow + + + + java.lang.String + String operations are generally safe. But Groovy is adding execute() method which is very dangerous. + allow + + execute + deny + + + + java.lang.CharSequence + allow + + + java.lang.Enum + allow + + + java.util.List + List operations are generally safe. But Groovy is adding execute() method which is very dangerous. + allow + + execute + deny + + + + java.util.ArrayList + List operations are generally safe. But Groovy is adding execute() method which is very dangerous. + allow + + execute + deny + + + + java.util.Map + allow + + + java.util.HashMap + allow + + + java.util.Date + allow + + + javax.xml.namespace.QName + allow + + + javax.xml.datatype.XMLGregorianCalendar + allow + + + java.lang.System + Just a few methods of System are safe enough. + deny + + currentTimeMillis + allow + + + + java.lang.IllegalStateException + Basic Java exception. Also used in test. + allow + + + java.lang.IllegalArgumentException + Basic Java exception. + allow + + + com.evolveum.midpoint.model.common.expression.functions.BasicExpressionFunctions + MidPoint basic functions library + allow + + + com.evolveum.midpoint.model.common.expression.functions.LogExpressionFunctions + MidPoint logging functions library + allow + + + com.evolveum.midpoint.report.impl.ReportFunctions + MidPoint report functions library + allow + + + org.apache.commons.lang.StringUtils + Apache Commons: Strings + allow + + + + + + c:UserType + + diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/Component.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/Component.java index c37c00700dc..15b0a994596 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/Component.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/Component.java @@ -6,8 +6,11 @@ */ package com.evolveum.midpoint.schrodinger.component; +import com.codeborne.selenide.ElementsCollection; import com.codeborne.selenide.SelenideElement; +import com.evolveum.midpoint.schrodinger.util.Schrodinger; + /** * Created by Viliam Repan (lazyman). */ @@ -37,4 +40,13 @@ public T getParent() { public SelenideElement getParentElement() { return parentElement; } + + protected SelenideElement getDisplayedElement(ElementsCollection elements) { + for (SelenideElement element : elements) { + if (element.isDisplayed()) { + return element; + } + } + return null; + } } diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/Popover.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/Popover.java index 3cf15ff3323..71698c2bf9a 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/Popover.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/Popover.java @@ -12,6 +12,8 @@ import com.evolveum.midpoint.schrodinger.component.Component; import com.evolveum.midpoint.schrodinger.util.Schrodinger; +import org.openqa.selenium.Keys; + /** * Created by matus on 3/22/2018. */ @@ -21,20 +23,28 @@ public Popover(T parent, SelenideElement parentElement) { } public Popover inputValue(String input) { - getParentElement().$(Schrodinger.byDataId("textInput")).waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).setValue(input); + getDisplayedElement(getParentElement().$$(Schrodinger.byDataId("textInput"))).waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).setValue(input); + + return this; + } + public Popover inputValueWithEnter(String input) { + SelenideElement inputField = getDisplayedElement(getParentElement().$$(Schrodinger.byDataId("textInput"))); + inputField.waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).setValue(input); + inputField.sendKeys(Keys.ENTER); return this; } public T updateSearch() { - getParentElement().$(Schrodinger.byDataId("update")).click(); - getParentElement().$(Schrodinger.byDataId("update")).waitUntil(Condition.disappears, MidPoint.TIMEOUT_DEFAULT_2_S); + SelenideElement button = getDisplayedElement(getParentElement().$$(Schrodinger.byDataId("update"))); + button.click(); + button.waitUntil(Condition.disappears, MidPoint.TIMEOUT_DEFAULT_2_S); return this.getParent(); } public T close() { - getParentElement().$(Schrodinger.byDataId("close")).click(); + getDisplayedElement(getParentElement().$$(Schrodinger.byDataId("searchSimple"))).click(); return this.getParent(); } diff --git a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/Search.java b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/Search.java index e51b526335a..cf6402d081a 100644 --- a/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/Search.java +++ b/tools/schrodinger/src/main/java/com/evolveum/midpoint/schrodinger/component/common/Search.java @@ -9,11 +9,13 @@ 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; import com.evolveum.midpoint.schrodinger.component.Component; import com.evolveum.midpoint.schrodinger.util.Schrodinger; +import org.openqa.selenium.By; /** * Created by Viliam Repan (lazyman). @@ -26,25 +28,20 @@ public Search(T parent, SelenideElement parentElement) { public Popover> byName() { + choiceBasicSearch(); + + getParentElement().$(Schrodinger.byDataId("a", "mainButton")).waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); + Selenide.sleep(2000); + return new Popover<>(this, getDisplayedPopover()); + } + + private void choiceBasicSearch() { SelenideElement linksContainer = getParentElement().$(Schrodinger.byDataId("div", "linksContainer")).waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S); try { linksContainer.$(Schrodinger.byDataId("a", "basic")).waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); } catch (Throwable t) { // all is ok, basic search is already selected option, TODO: Schrodinger should provide easy method to check component existence } - - getParentElement().$(Schrodinger.byDataId("a", "mainButton")).waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); - ElementsCollection popoverElements = getParentElement().$$(Schrodinger.byDataId("popover")); - - SelenideElement popover = null; - for (SelenideElement popoverElement : popoverElements) { - if (popoverElement.isDisplayed()) { - popover = popoverElement; - break; - } - popover = popoverElement; - } - return new Popover<>(this, popover); } public InputBox> byFullText() { @@ -60,5 +57,59 @@ public InputBox> byFullText() { SelenideElement fullTextField = getParentElement().$(Schrodinger.byDataId("input", "fullTextField")).waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S); return new InputBox<> (this, fullTextField); } + + public Search addSearchItem(String name) { + choiceBasicSearch(); + getParentElement().$(Schrodinger.byDataId("a", "more")).waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); + Selenide.sleep(2000); + SelenideElement popover = getDisplayedPopover(); + popover.$(Schrodinger.byDataId("input", "addText")).setValue(name); + Selenide.sleep(2000); + popover.$(Schrodinger.byDataId("a", "propLink")).click(); + return this; + } + + public Popover> byItem(String name) { + + choiceBasicSearch(); + + SelenideElement item = getItemByName(name); + if (item == null) { + addSearchItem(name); + Selenide.sleep(2000); + } + item = getItemByName(name); + if (item == null) { + throw new IllegalStateException("Couldn't find search item for name " + name); + } + + item.waitUntil(Condition.appears, MidPoint.TIMEOUT_DEFAULT_2_S).click(); + Selenide.sleep(2000); + return new Popover<>(this, getDisplayedPopover()); + } + + private SelenideElement getItemByName(String name) { + ElementsCollection items = getParentElement().findAll(Schrodinger.byDataId("a", "mainButton")); + for (SelenideElement item : items) { + if (item.getText().startsWith(name + ":")) { + return item; + } + } + return null; + } + + private SelenideElement getDisplayedPopover() { + ElementsCollection popoverElements = getParentElement().$$(Schrodinger.byDataId("popover")); + SelenideElement popover = null; + for (SelenideElement popoverElement : popoverElements) { + if (popoverElement.isDisplayed()) { + popover = popoverElement; + break; + } + popover = popoverElement; + } + return popover; + } + }