diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.html
index fb2b88afa7c..c166236d159 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.html
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.html
@@ -17,6 +17,7 @@
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.java
index f22563fd55d..9d65637db61 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.java
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/configuration/InternalsCachePanel.java
@@ -10,6 +10,8 @@
import com.evolveum.midpoint.util.Holder;
import com.evolveum.midpoint.util.KeyValueTreeNode;
import com.evolveum.midpoint.util.TreeNode;
+import com.evolveum.midpoint.util.logging.Trace;
+import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.component.AceEditor;
import com.evolveum.midpoint.web.component.AjaxButton;
import com.evolveum.midpoint.web.security.MidPointApplication;
@@ -24,11 +26,16 @@
import java.util.ArrayList;
import java.util.List;
+import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
+
public class InternalsCachePanel extends BasePanel{
private static final long serialVersionUID = 1L;
+ private static final Trace LOGGER = TraceManager.getTrace(InternalsCachePanel.class);
+
private static final String ID_CLEAR_CACHES_BUTTON = "clearCaches";
+ private static final String ID_DUMP_CONTENT_BUTTON = "dumpContent";
private static final String ID_INFORMATION = "information";
public InternalsCachePanel(String id) {
@@ -58,7 +65,7 @@ public void setObject(String object) {
informationText.setMode(null);
add(informationText);
- AjaxButton clearCaches = new AjaxButton(ID_CLEAR_CACHES_BUTTON, createStringResource("InternalsCachePanel.button.clearCaches")) {
+ add(new AjaxButton(ID_CLEAR_CACHES_BUTTON, createStringResource("InternalsCachePanel.button.clearCaches")) {
private static final long serialVersionUID = 1L;
@@ -67,54 +74,89 @@ public void onClick(AjaxRequestTarget target) {
getPageBase().getCacheDispatcher().dispatchInvalidation(null, null, true, null);
target.add(InternalsCachePanel.this);
}
- };
+ });
+
+ add(new AjaxButton(ID_DUMP_CONTENT_BUTTON, createStringResource("InternalsCachePanel.button.dumpContent")) {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onClick(AjaxRequestTarget target) {
+ String cacheInformation = getCacheInformation();
+ LOGGER.info("Dumping the content of the caches.\nCurrent counters:\n{}\n", cacheInformation);
+ MidPointApplication.get().getCacheRegistry().dumpContent();
- add(clearCaches);
+ getSession().success(getPageBase().getString("InternalsCachePanel.result.dumped"));
+ target.add(getPageBase());
+ }
+ });
+ }
+
+ private static class SizeInformation {
+ private final int size;
+ private final int secondarySize;
+
+ private SizeInformation(SingleCacheStateInformationType info) {
+ size = defaultIfNull(info.getSize(), 0);
+ secondarySize = defaultIfNull(info.getSecondarySize(), 0);
+ }
+
+ private SizeInformation(ComponentSizeInformationType info) {
+ size = defaultIfNull(info.getSize(), 0);
+ secondarySize = defaultIfNull(info.getSecondarySize(), 0);
+ }
}
private String getCacheInformation() {
StringBuilder sb = new StringBuilder();
- MidPointApplication midPointApplication = MidPointApplication.get();
- if (midPointApplication != null) {
- CachesStateInformationType state = midPointApplication.getCacheRegistry().getStateInformation();
- List> trees = new ArrayList<>();
- for (SingleCacheStateInformationType entry : state.getEntry()) {
- KeyValueTreeNode root = new KeyValueTreeNode<>(entry.getName(), entry.getSize());
- trees.add(root);
- addComponents(root, entry.getComponent());
- }
- Holder maxLabelLength = new Holder<>(0);
- Holder maxSize = new Holder<>(0);
- trees.forEach(tree -> tree.acceptDepthFirst(node -> {
- int labelLength = node.getUserObject().getKey().length() + node.getDepth() * 2;
- Integer size = node.getUserObject().getValue();
- if (labelLength > maxLabelLength.getValue()) {
- maxLabelLength.setValue(labelLength);
- }
- if (size != null && size > maxSize.getValue()) {
- maxSize.setValue(size);
- }
- }));
- int labelSize = Math.max(maxLabelLength.getValue() + 1, 30);
- int sizeSize = Math.max((int) Math.log10(maxSize.getValue()) + 2, 7);
- int firstPart = labelSize + 3;
- int secondPart = sizeSize + 3;
- String headerFormatString = " %-" + labelSize + "s | %" + sizeSize + "s \n";
- String valueFormatString = " %-" + labelSize + "s | %" + sizeSize + "d \n";
- //System.out.println("valueFormatString = " + valueFormatString);
- sb.append("\n");
- sb.append(String.format(headerFormatString, "Cache", "Size"));
- sb.append(StringUtils.repeat("=", firstPart)).append("+").append(StringUtils.repeat("=", secondPart)).append("\n");
- trees.forEach(tree -> {
- tree.acceptDepthFirst(node -> printNode(sb, valueFormatString, node));
- sb.append(StringUtils.repeat("-", firstPart)).append("+").append(StringUtils.repeat("-", secondPart)).append("\n");
- });
+ CachesStateInformationType state = MidPointApplication.get().getCacheRegistry().getStateInformation();
+ List> trees = new ArrayList<>();
+ for (SingleCacheStateInformationType entry : state.getEntry()) {
+ KeyValueTreeNode root = new KeyValueTreeNode<>(entry.getName(), new SizeInformation(entry));
+ trees.add(root);
+ addComponents(root, entry.getComponent());
}
+ Holder maxLabelLength = new Holder<>(0);
+ Holder maxSize = new Holder<>(1); // to avoid issues with log10
+ Holder maxSecondarySize = new Holder<>(1); // to avoid issues with log10
+ trees.forEach(tree -> tree.acceptDepthFirst(node -> {
+ int labelLength = node.getUserObject().getKey().length() + node.getDepth() * 2;
+ int size = node.getUserObject().getValue().size;
+ int secondarySize = node.getUserObject().getValue().secondarySize;
+ if (labelLength > maxLabelLength.getValue()) {
+ maxLabelLength.setValue(labelLength);
+ }
+ if (size > maxSize.getValue()) {
+ maxSize.setValue(size);
+ }
+ if (secondarySize > maxSecondarySize.getValue()) {
+ maxSecondarySize.setValue(secondarySize);
+ }
+ }));
+ int labelSize = Math.max(maxLabelLength.getValue() + 1, 30);
+ int sizeSize = Math.max((int) Math.log10(maxSize.getValue()) + 2, 7);
+ int secondarySizeSize = Math.max((int) Math.log10(maxSecondarySize.getValue()) + 2, 8);
+ int firstPart = labelSize + 3;
+ int secondPart = sizeSize + 2;
+ int thirdPart = secondarySizeSize + 3;
+ String headerFormatString = " %-" + labelSize + "s | %" + sizeSize + "s | %" + secondarySizeSize + "s\n";
+ String valueFormatString = " %-" + labelSize + "s | %" + sizeSize + "d | %" + secondarySizeSize + "s\n";
+ sb.append("\n");
+ sb.append(String.format(headerFormatString, "Cache", "Size", "Sec. size"));
+ sb.append(StringUtils.repeat("=", firstPart)).append("+")
+ .append(StringUtils.repeat("=", secondPart)).append("+")
+ .append(StringUtils.repeat("=", thirdPart)).append("\n");
+ trees.forEach(tree -> {
+ tree.acceptDepthFirst(node -> printNode(sb, valueFormatString, node));
+ sb.append(StringUtils.repeat("-", firstPart)).append("+")
+ .append(StringUtils.repeat("-", secondPart)).append("+")
+ .append(StringUtils.repeat("-", thirdPart)).append("\n");
+ });
return sb.toString();
}
- private void printNode(StringBuilder sb, String formatString, TreeNode> node) {
- Pair pair = node.getUserObject();
+ private void printNode(StringBuilder sb, String formatString, TreeNode> node) {
+ Pair pair = node.getUserObject();
if (pair != null) {
int depth = node.getDepth();
StringBuilder prefix = new StringBuilder();
@@ -124,13 +166,22 @@ private void printNode(StringBuilder sb, String formatString, TreeNode node, List components) {
+ private void addComponents(KeyValueTreeNode node, List components) {
for (ComponentSizeInformationType component : components) {
- KeyValueTreeNode child = node.createChild(component.getName(), component.getSize());
+ KeyValueTreeNode child = node.createChild(component.getName(), new SizeInformation(component));
addComponents(child, component.getComponent());
}
}
diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/match/MatchingRule.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/match/MatchingRule.java
index a5279fa0bde..eb8adecddbd 100644
--- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/match/MatchingRule.java
+++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/match/MatchingRule.java
@@ -29,7 +29,7 @@ public interface MatchingRule {
/**
* Returns true if the rule can be applied to the specified XSD type.
*/
- boolean isSupported(QName xsdType);
+ boolean supports(QName xsdType);
/**
* Matches two objects.
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DefaultMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DefaultMatchingRule.java
index f4d6fed21cb..c9f161c8fc2 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DefaultMatchingRule.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DefaultMatchingRule.java
@@ -34,7 +34,7 @@ public QName getName() {
* @see com.evolveum.midpoint.model.match.MatchingRule#isSupported(java.lang.Class, javax.xml.namespace.QName)
*/
@Override
- public boolean isSupported(QName xsdType) {
+ public boolean supports(QName xsdType) {
// We support everything. We are the default.
return true;
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DistinguishedNameMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DistinguishedNameMatchingRule.java
index 04bce44cb0e..695ac646394 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DistinguishedNameMatchingRule.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/DistinguishedNameMatchingRule.java
@@ -34,7 +34,7 @@ public QName getName() {
}
@Override
- public boolean isSupported(QName xsdType) {
+ public boolean supports(QName xsdType) {
return (DOMUtil.XSD_STRING.equals(xsdType));
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/ExchangeEmailAddressesMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/ExchangeEmailAddressesMatchingRule.java
index f4dc479a76a..ef56cc019f4 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/ExchangeEmailAddressesMatchingRule.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/ExchangeEmailAddressesMatchingRule.java
@@ -29,7 +29,7 @@ public QName getName() {
}
@Override
- public boolean isSupported(QName xsdType) {
+ public boolean supports(QName xsdType) {
return (DOMUtil.XSD_STRING.equals(xsdType));
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/MatchingRuleRegistryImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/MatchingRuleRegistryImpl.java
index 9086ca12fc8..af2fa677d8a 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/MatchingRuleRegistryImpl.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/MatchingRuleRegistryImpl.java
@@ -23,7 +23,7 @@
public class MatchingRuleRegistryImpl implements MatchingRuleRegistry {
@NotNull private final MatchingRule> defaultMatchingRule;
- @NotNull private final Map> matchingRules = new HashMap<>();
+ @NotNull private final Map> matchingRules = new HashMap<>();
MatchingRuleRegistryImpl() {
this.defaultMatchingRule = new DefaultMatchingRule<>();
@@ -34,27 +34,39 @@ public class MatchingRuleRegistryImpl implements MatchingRuleRegistry {
@NotNull
public MatchingRule getMatchingRule(QName ruleName, QName typeQName) throws SchemaException {
if (ruleName == null) {
+ //noinspection unchecked
return (MatchingRule) defaultMatchingRule;
}
- MatchingRule matchingRule = (MatchingRule) matchingRules.get(ruleName);
- if (matchingRule == null) {
- //try match according to the localPart
- if (QNameUtil.matchAny(ruleName, matchingRules.keySet())){
- ruleName = QNameUtil.resolveNs(ruleName, matchingRules.keySet());
- matchingRule = (MatchingRule) matchingRules.get(ruleName);
- }
- if (matchingRule == null) {
- throw new SchemaException("Unknown matching rule for name " + ruleName);
- }
- }
- if (typeQName != null && !matchingRule.isSupported(typeQName)) {
- throw new SchemaException("Matching rule "+ruleName+" does not support type "+typeQName);
+ MatchingRule matchingRule = findMatchingRule(ruleName);
+ if (typeQName == null || matchingRule.supports(typeQName)) {
+ return matchingRule;
+ } else {
+ throw new SchemaException("Matching rule " + ruleName + " does not support type " + typeQName);
}
- return matchingRule;
}
- void registerMatchingRule(MatchingRule rule) {
- ((Map)this.matchingRules).put(rule.getName(), rule);
+ @NotNull
+ private MatchingRule findMatchingRule(QName ruleName) throws SchemaException {
+ //noinspection unchecked
+ MatchingRule rule = (MatchingRule) matchingRules.get(ruleName);
+ if (rule != null) {
+ return rule;
+ }
+
+ // try match according to the localPart
+ QName qualifiedRuleName = QNameUtil.resolveNs(ruleName, matchingRules.keySet());
+ if (qualifiedRuleName != null) {
+ //noinspection unchecked
+ MatchingRule ruleAfterQualification = (MatchingRule) matchingRules.get(qualifiedRuleName);
+ if (ruleAfterQualification != null) {
+ return ruleAfterQualification;
+ }
+ }
+
+ throw new SchemaException("Couldn't find matching rule named '" + ruleName + "'");
}
+ void registerMatchingRule(MatchingRule> rule) {
+ matchingRules.put(rule.getName(), rule);
+ }
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringNormMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringNormMatchingRule.java
index dd34480f81e..db5b6f3b431 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringNormMatchingRule.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringNormMatchingRule.java
@@ -34,7 +34,7 @@ public QName getName() {
* @see com.evolveum.midpoint.prism.match.MatchingRule#isSupported(java.lang.Class, javax.xml.namespace.QName)
*/
@Override
- public boolean isSupported(QName xsdType) {
+ public boolean supports(QName xsdType) {
return (PolyStringType.COMPLEX_TYPE.equals(xsdType));
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringOrigMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringOrigMatchingRule.java
index e28d387b903..384c105f865 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringOrigMatchingRule.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringOrigMatchingRule.java
@@ -34,7 +34,7 @@ public QName getName() {
* @see com.evolveum.midpoint.prism.match.MatchingRule#isSupported(java.lang.Class, javax.xml.namespace.QName)
*/
@Override
- public boolean isSupported(QName xsdType) {
+ public boolean supports(QName xsdType) {
return (PolyStringType.COMPLEX_TYPE.equals(xsdType));
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringStrictMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringStrictMatchingRule.java
index 767fc0f5def..07876a39936 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringStrictMatchingRule.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/PolyStringStrictMatchingRule.java
@@ -13,7 +13,6 @@
import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.match.MatchingRule;
import com.evolveum.midpoint.prism.polystring.PolyString;
-import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
/**
@@ -34,7 +33,7 @@ public QName getName() {
* @see com.evolveum.midpoint.prism.match.MatchingRule#isSupported(java.lang.Class, javax.xml.namespace.QName)
*/
@Override
- public boolean isSupported(QName xsdType) {
+ public boolean supports(QName xsdType) {
return (PolyStringType.COMPLEX_TYPE.equals(xsdType));
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/StringIgnoreCaseMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/StringIgnoreCaseMatchingRule.java
index a10dceffc5d..db8765ba503 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/StringIgnoreCaseMatchingRule.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/StringIgnoreCaseMatchingRule.java
@@ -31,7 +31,7 @@ public QName getName() {
}
@Override
- public boolean isSupported(QName xsdType) {
+ public boolean supports(QName xsdType) {
return (DOMUtil.XSD_STRING.equals(xsdType));
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/UuidMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/UuidMatchingRule.java
index 05a6d9ede61..65a12fbd372 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/UuidMatchingRule.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/UuidMatchingRule.java
@@ -33,7 +33,7 @@ public QName getName() {
}
@Override
- public boolean isSupported(QName xsdType) {
+ public boolean supports(QName xsdType) {
return (DOMUtil.XSD_STRING.equals(xsdType));
}
diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/XmlMatchingRule.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/XmlMatchingRule.java
index 1ced4e948ca..a5449d2949b 100644
--- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/XmlMatchingRule.java
+++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/match/XmlMatchingRule.java
@@ -37,7 +37,7 @@ public QName getName() {
}
@Override
- public boolean isSupported(QName xsdType) {
+ public boolean supports(QName xsdType) {
return (DOMUtil.XSD_STRING.equals(xsdType));
}
diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/expression/ExpressionProfiles.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/expression/ExpressionProfiles.java
index d3e24eabc6e..8e348f56b71 100644
--- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/expression/ExpressionProfiles.java
+++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/expression/ExpressionProfiles.java
@@ -6,8 +6,9 @@
*/
package com.evolveum.midpoint.schema.expression;
-import java.util.HashMap;
+import java.util.Collections;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* @author semancik
@@ -15,7 +16,7 @@
*/
public class ExpressionProfiles {
- private final Map profiles = new HashMap<>();
+ private final Map profiles = new ConcurrentHashMap<>();
public ExpressionProfile getProfile(String identifier) {
return profiles.get(identifier);
@@ -28,4 +29,8 @@ public void add(ExpressionProfile profile) {
public int size() {
return profiles.size();
}
+
+ public Map getProfiles() {
+ return Collections.unmodifiableMap(profiles);
+ }
}
diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd
index 3d7be668652..32ecd3a5135 100755
--- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd
+++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd
@@ -26487,6 +26487,14 @@
+
+
+
+ Current "secondary size" of the cache, if applicable. This can mean e.g. number of cached objects
+ for query caches.
+
+
+
@@ -26531,6 +26539,13 @@
+
+
+
+ Component secondary size (if applicable).
+
+
+
diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/QNameUtil.java b/infra/util/src/main/java/com/evolveum/midpoint/util/QNameUtil.java
index 702293bfbef..d44f0f000eb 100644
--- a/infra/util/src/main/java/com/evolveum/midpoint/util/QNameUtil.java
+++ b/infra/util/src/main/java/com/evolveum/midpoint/util/QNameUtil.java
@@ -259,15 +259,14 @@ public static boolean matchWithUri(QName qname, String uri) {
return match(qname, uriToQName(uri, true));
}
-
- public static QName resolveNs(QName a, Collection col){
+ public static QName resolveNs(QName a, Collection col) {
if (col == null) {
return null;
}
QName found = null;
for (QName b: col) {
if (match(a, b)) {
- if (found != null){
+ if (found != null) {
throw new IllegalStateException("Found more than one suitable qnames( "+ found + b + ") for attribute: " + a);
}
found = b;
diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java b/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java
index 7d1993fa690..bb5f3e258cd 100644
--- a/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java
+++ b/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java
@@ -168,4 +168,10 @@ public static int getTotalSize(ConcurrentHa
}
protected abstract int getSize();
+
+ public static void dumpContent(ConcurrentHashMap cacheInstances) {
+ cacheInstances.forEach((thread, cache) -> cache.dumpContent(thread.getName()));
+ }
+
+ protected abstract void dumpContent(String threadName);
}
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/SystemObjectCache.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/SystemObjectCache.java
index 960a24cfaf5..915bddd1b49 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/SystemObjectCache.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/SystemObjectCache.java
@@ -10,6 +10,7 @@
import com.evolveum.midpoint.model.common.expression.ExpressionProfileCompiler;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
+import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.repo.cache.CacheRegistry;
import com.evolveum.midpoint.repo.api.Cacheable;
@@ -56,6 +57,7 @@
public class SystemObjectCache implements Cacheable {
private static final Trace LOGGER = TraceManager.getTrace(SystemObjectCache.class);
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(SystemObjectCache.class.getName() + ".content");
@Autowired
@Qualifier("cacheRepositoryService")
@@ -69,7 +71,7 @@ public class SystemObjectCache implements Cacheable {
private PrismObject securityPolicy;
private Long securityPolicyCheckTimestamp;
- private ExpressionProfiles expressionProfiles;
+ private volatile ExpressionProfiles expressionProfiles;
@PostConstruct
public void register() {
@@ -217,6 +219,7 @@ public synchronized void invalidateCaches() {
securityPolicyCheckTimestamp = null;
}
+ @SuppressWarnings("WeakerAccess")
public PrismObject getArchetype(String oid, OperationResult result) throws ObjectNotFoundException, SchemaException {
// TODO: make this efficient (use cache)
return cacheRepositoryService.getObject(ArchetypeType.class, oid, null, result);
@@ -262,6 +265,7 @@ public void invalidate(Class> type, String oid, CacheInvalidationContext conte
if (type == null || type.isAssignableFrom(SystemConfigurationType.class)) {
invalidateCaches();
}
+ expressionProfiles = null;
}
@NotNull
@@ -274,7 +278,28 @@ public Collection getStateInformation() {
}
private int getSize() {
+ ExpressionProfiles cachedProfiles = this.expressionProfiles;
+
return (systemConfiguration != null ? 1 : 0) +
- (expressionProfiles != null ? expressionProfiles.size() : 0);
+ (cachedProfiles != null ? cachedProfiles.size() : 0);
+ }
+
+ @Override
+ public void dumpContent() {
+ if (LOGGER_CONTENT.isInfoEnabled()) {
+ PrismObject cachedConfiguration = this.systemConfiguration;
+ if (cachedConfiguration != null) {
+ LOGGER_CONTENT.info("Cached system configuration: {} (version {}); systemConfigurationCheckTimestamp: {}",
+ cachedConfiguration, cachedConfiguration.getVersion(),
+ XmlTypeConverter.createXMLGregorianCalendar(systemConfigurationCheckTimestamp));
+ } else {
+ LOGGER_CONTENT.info("No cached system configuration");
+ }
+
+ ExpressionProfiles cachedProfiles = this.expressionProfiles;
+ if (cachedProfiles != null) {
+ cachedProfiles.getProfiles().forEach((k, v) -> LOGGER_CONTENT.info("Cached expression profile: {}: {}", k, v));
+ }
+ }
}
}
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/caching/AbstractSearchExpressionEvaluatorCache.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/caching/AbstractSearchExpressionEvaluatorCache.java
index eb5dea20b36..0fe5ac09c99 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/caching/AbstractSearchExpressionEvaluatorCache.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/caching/AbstractSearchExpressionEvaluatorCache.java
@@ -41,6 +41,7 @@ public abstract class AbstractSearchExpressionEvaluatorCache LOGGER.info("Cached search expression evaluation [{}] {}: {}", threadName, qk, qr));
+ }
+ }
}
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/FunctionLibrary.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/FunctionLibrary.java
index 4c3252d99f7..96780701a9e 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/FunctionLibrary.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/FunctionLibrary.java
@@ -40,5 +40,12 @@ public void setGenericFunctions(Object genericFunctions) {
this.genericFunctions = genericFunctions;
}
-
+ @Override
+ public String toString() {
+ return "FunctionLibrary{" +
+ "variableName='" + variableName + '\'' +
+ ", namespace='" + namespace + '\'' +
+ ", genericFunctions=" + genericFunctions +
+ '}';
+ }
}
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionEvaluatorFactory.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionEvaluatorFactory.java
index b64e1b4934b..c84d0870164 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionEvaluatorFactory.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionEvaluatorFactory.java
@@ -82,7 +82,8 @@ public ExpressionEvaluator
}
ScriptExpressionEvaluatorType scriptType = (ScriptExpressionEvaluatorType) evaluatorElementObject;
- ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(scriptType, outputDefinition, expressionProfile, factory, contextDescription, task, result);
+ ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(scriptType, outputDefinition,
+ expressionProfile, factory, contextDescription, result);
return new ScriptExpressionEvaluator(ELEMENT_NAME, scriptType, outputDefinition, protector, prismContext, scriptExpression, securityContextManager, localizationService);
diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionFactory.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionFactory.java
index 286e43881bd..24aa9e55b97 100644
--- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionFactory.java
+++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/ScriptExpressionFactory.java
@@ -7,6 +7,8 @@
package com.evolveum.midpoint.model.common.expression.script;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -17,7 +19,6 @@
import com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary;
import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.PrismContext;
-import com.evolveum.midpoint.prism.crypto.Protector;
import com.evolveum.midpoint.repo.api.Cacheable;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.repo.cache.CacheRegistry;
@@ -34,7 +35,6 @@
import com.evolveum.midpoint.schema.expression.ScriptExpressionProfile;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.MiscSchemaUtil;
-import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SecurityViolationException;
import com.evolveum.midpoint.util.logging.Trace;
@@ -49,17 +49,18 @@
public class ScriptExpressionFactory implements Cacheable {
private static final Trace LOGGER = TraceManager.getTrace(ScriptExpressionFactory.class);
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(ScriptExpressionFactory.class.getName() + ".content");
- public static final String DEFAULT_LANGUAGE = "http://midpoint.evolveum.com/xml/ns/public/expression/language#Groovy";
+ private static final String DEFAULT_LANGUAGE = "http://midpoint.evolveum.com/xml/ns/public/expression/language#Groovy";
- private Map evaluatorMap = new HashMap<>();
+ private final Map evaluatorMap = new HashMap<>();
private ObjectResolver objectResolver;
private final PrismContext prismContext;
private Collection functions;
- private final Protector protector;
private final RepositoryService repositoryService; // might be null during low-level testing
- private Map customFunctionLibraryCache;
+ @NotNull private final Map customFunctionLibraryCache = new ConcurrentHashMap<>();
+ private final AtomicBoolean initialized = new AtomicBoolean(false);
private CacheRegistry cacheRegistry;
@@ -73,9 +74,8 @@ public void unregister() {
cacheRegistry.unregisterCacheableService(this);
}
- public ScriptExpressionFactory(PrismContext prismContext, Protector protector, RepositoryService repositoryService) {
+ public ScriptExpressionFactory(PrismContext prismContext, RepositoryService repositoryService) {
this.prismContext = prismContext;
- this.protector = protector;
this.repositoryService = repositoryService;
}
@@ -105,10 +105,6 @@ public Map getEvaluators() {
return evaluatorMap;
}
- public CacheRegistry geCacheRegistry() {
- return cacheRegistry;
- }
-
public void setCacheRegistry(CacheRegistry registry) {
this.cacheRegistry = registry;
}
@@ -116,10 +112,10 @@ public void setCacheRegistry(CacheRegistry registry) {
public ScriptExpression createScriptExpression(
ScriptExpressionEvaluatorType expressionType, ItemDefinition outputDefinition,
ExpressionProfile expressionProfile, ExpressionFactory expressionFactory,
- String shortDesc, Task task, OperationResult result)
+ String shortDesc, OperationResult result)
throws ExpressionSyntaxException, SecurityViolationException {
- initializeCustomFunctionsLibraryCache(expressionFactory, result);
+ initializeCustomFunctionsLibraryCacheIfNeeded(expressionFactory, result);
//cache cleanup method
String language = getLanguage(expressionType);
@@ -167,39 +163,41 @@ private ScriptExpressionProfile processScriptExpressionProfile(ExpressionProfile
return scriptProfile;
}
- // if performance becomes an issue, replace 'synchronized' with something more elaborate
- private synchronized void initializeCustomFunctionsLibraryCache(ExpressionFactory expressionFactory,
- OperationResult result) throws ExpressionSyntaxException {
- if (customFunctionLibraryCache != null) {
- return;
+ private void initializeCustomFunctionsLibraryCacheIfNeeded(ExpressionFactory expressionFactory, OperationResult result)
+ throws ExpressionSyntaxException {
+ if (initialized.compareAndSet(false, true)) {
+ initializeCustomFunctionsLibraryCache(expressionFactory, result);
}
- customFunctionLibraryCache = new HashMap<>();
- if (repositoryService == null) {
+ }
+
+ private void initializeCustomFunctionsLibraryCache(ExpressionFactory expressionFactory, OperationResult result)
+ throws ExpressionSyntaxException {
+ if (repositoryService != null) {
+ OperationResult subResult = result
+ .createMinorSubresult(ScriptExpressionFactory.class.getName() + ".searchCustomFunctions");
+ ResultHandler functionLibraryHandler = (object, parentResult) -> {
+ // TODO: determine profile from function library archetype
+ ExpressionProfile expressionProfile = MiscSchemaUtil.getExpressionProfile();
+ FunctionLibrary customLibrary = new FunctionLibrary();
+ customLibrary.setVariableName(object.getName().getOrig());
+ customLibrary.setGenericFunctions(
+ new CustomFunctions(object.asObjectable(), expressionFactory, expressionProfile));
+ customLibrary.setNamespace(MidPointConstants.NS_FUNC_CUSTOM);
+ customFunctionLibraryCache.put(object.getName().getOrig(), customLibrary);
+ return true;
+ };
+ try {
+ repositoryService.searchObjectsIterative(FunctionLibraryType.class, null, functionLibraryHandler,
+ SelectorOptions.createCollection(GetOperationOptions.createReadOnly()), true, subResult);
+ subResult.recordSuccessIfUnknown();
+ } catch (SchemaException | RuntimeException e) {
+ subResult.recordFatalError("Failed to initialize custom functions", e);
+ throw new ExpressionSyntaxException(
+ "An error occurred during custom libraries initialization. " + e.getMessage(), e);
+ }
+ } else {
LOGGER.warn("No repository service set for ScriptExpressionFactory; custom functions will not be loaded. This"
+ " can occur during low-level testing; never during standard system execution.");
- return;
- }
- OperationResult subResult = result
- .createMinorSubresult(ScriptExpressionFactory.class.getName() + ".searchCustomFunctions");
- ResultHandler functionLibraryHandler = (object, parentResult) -> {
- // TODO: determine profile from function library archetype
- ExpressionProfile expressionProfile = MiscSchemaUtil.getExpressionProfile();
- FunctionLibrary customLibrary = new FunctionLibrary();
- customLibrary.setVariableName(object.getName().getOrig());
- customLibrary.setGenericFunctions(
- new CustomFunctions(object.asObjectable(), expressionFactory, expressionProfile));
- customLibrary.setNamespace(MidPointConstants.NS_FUNC_CUSTOM);
- customFunctionLibraryCache.put(object.getName().getOrig(), customLibrary);
- return true;
- };
- try {
- repositoryService.searchObjectsIterative(FunctionLibraryType.class, null, functionLibraryHandler,
- SelectorOptions.createCollection(GetOperationOptions.createReadOnly()), true, subResult);
- subResult.recordSuccessIfUnknown();
- } catch (SchemaException | RuntimeException e) {
- subResult.recordFatalError("Failed to initialize custom functions", e);
- throw new ExpressionSyntaxException(
- "An error occurred during custom libraries initialization. " + e.getMessage(), e);
}
}
@@ -229,7 +227,7 @@ private String getLanguage(ScriptExpressionEvaluatorType expressionType) {
public void invalidate(Class> type, String oid, CacheInvalidationContext context) {
if (type == null || type.isAssignableFrom(FunctionLibraryType.class)) {
// Currently we don't try to select entries to be cleared based on OID
- customFunctionLibraryCache = null;
+ customFunctionLibraryCache.clear();
}
}
@@ -238,7 +236,17 @@ public void invalidate(Class> type, String oid, CacheInvalidationContext conte
public Collection getStateInformation() {
return Collections.singleton(new SingleCacheStateInformationType(prismContext)
.name(ScriptExpressionFactory.class.getName())
- .size(customFunctionLibraryCache != null ? customFunctionLibraryCache.size() : 0));
+ .size(customFunctionLibraryCache.size()));
}
-}
+ @Override
+ public void dumpContent() {
+ if (LOGGER_CONTENT.isInfoEnabled()) {
+ if (initialized.get()) {
+ customFunctionLibraryCache.forEach((k, v) -> LOGGER_CONTENT.info("Cached function library: {}: {}", k, v));
+ } else {
+ LOGGER_CONTENT.info("Custom function library cache is not yet initialized");
+ }
+ }
+ }
+}
diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java
index aa033fa0a49..f7f98a2e6b0 100644
--- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java
+++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java
@@ -90,7 +90,7 @@ public static ExpressionFactory createInitializedExpressionFactory(ObjectResolve
Collection functions = new ArrayList<>();
functions.add(FunctionLibraryUtil.createBasicFunctionLibrary(prismContext, protector, clock));
functions.add(FunctionLibraryUtil.createLogFunctionLibrary(prismContext));
- ScriptExpressionFactory scriptExpressionFactory = new ScriptExpressionFactory(prismContext, protector, repositoryService);
+ ScriptExpressionFactory scriptExpressionFactory = new ScriptExpressionFactory(prismContext, repositoryService);
scriptExpressionFactory.setObjectResolver(resolver);
scriptExpressionFactory.setFunctions(functions);
diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java
index 24e96436ae4..fad5b31d03f 100644
--- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java
+++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java
@@ -85,7 +85,7 @@ public void setupFactory() {
Clock clock = new Clock();
Collection functions = new ArrayList<>();
functions.add(FunctionLibraryUtil.createBasicFunctionLibrary(prismContext, protector, clock));
- scriptExpressionfactory = new ScriptExpressionFactory(prismContext, protector, null);
+ scriptExpressionfactory = new ScriptExpressionFactory(prismContext, null);
scriptExpressionfactory.setObjectResolver(resolver);
scriptExpressionfactory.setFunctions(functions);
localizationService = LocalizationTestUtil.getLocalizationService();
diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java
index 21fe56a9e2b..719547c0c43 100644
--- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java
+++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java
@@ -80,7 +80,7 @@ public void setupFactory() {
Clock clock = new Clock();
Collection functions = new ArrayList<>();
functions.add(FunctionLibraryUtil.createBasicFunctionLibrary(prismContext, protector, clock));
- scriptExpressionfactory = new ScriptExpressionFactory(prismContext, protector, null);
+ scriptExpressionfactory = new ScriptExpressionFactory(prismContext, null);
scriptExpressionfactory.setObjectResolver(resolver);
scriptExpressionfactory.setFunctions(functions);
evaluator = new Jsr223ScriptEvaluator("groovy", prismContext, protector, LocalizationTestUtil.getLocalizationService());
diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/triggerSetter/TriggerCreatorGlobalState.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/triggerSetter/TriggerCreatorGlobalState.java
index 7445cdab95e..1b0cae48c40 100644
--- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/triggerSetter/TriggerCreatorGlobalState.java
+++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/triggerSetter/TriggerCreatorGlobalState.java
@@ -8,6 +8,7 @@
package com.evolveum.midpoint.model.impl.expr.triggerSetter;
import com.evolveum.midpoint.CacheInvalidationContext;
+import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionFactory;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.repo.api.Cacheable;
import com.evolveum.midpoint.repo.api.DeleteObjectResult;
@@ -36,6 +37,7 @@
public class TriggerCreatorGlobalState implements Cacheable {
private static final Trace LOGGER = TraceManager.getTrace(TriggerCreatorGlobalState.class);
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(TriggerCreatorGlobalState.class.getName() + ".content");
private AtomicLong lastExpirationCleanup = new AtomicLong(0L);
@@ -107,6 +109,13 @@ public Collection getStateInformation() {
);
}
+ @Override
+ public void dumpContent() {
+ if (LOGGER_CONTENT.isInfoEnabled()) {
+ state.forEach((k, v) -> LOGGER_CONTENT.info("Cached trigger creation: {}: {}", k, v));
+ }
+ }
+
@PostConstruct
public void register() {
cacheRegistry.registerCacheableService(this);
diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkHookHelper.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkHookHelper.java
index 42699f046b9..9ed7fd6ab1b 100644
--- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkHookHelper.java
+++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ClockworkHookHelper.java
@@ -153,7 +153,8 @@ private void evaluateScriptingHook(LensContext> context,
LOGGER.trace("Evaluating {}", shortDesc);
// TODO: it would be nice to cache this
// null output definition: this script has no output
- ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(scriptExpressionEvaluatorType, null, context.getPrivilegedExpressionProfile(), expressionFactory, shortDesc, task, result);
+ ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(scriptExpressionEvaluatorType, null,
+ context.getPrivilegedExpressionProfile(), expressionFactory, shortDesc, result);
ExpressionVariables variables = new ExpressionVariables();
variables.put(ExpressionConstants.VAR_PRISM_CONTEXT, prismContext, PrismContext.class);
diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusConstraintsChecker.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusConstraintsChecker.java
index e289467da32..72e799b99f6 100644
--- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusConstraintsChecker.java
+++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/focus/FocusConstraintsChecker.java
@@ -7,7 +7,6 @@
package com.evolveum.midpoint.model.impl.lens.projector.focus;
import java.util.Collection;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -231,6 +230,8 @@ public static void clearCacheForDelta(Collection extends ItemDelta> modificati
public static class Cache extends AbstractThreadLocalCache {
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(Cache.class.getName() + ".content");
+
private final Set conflictFreeNames = ConcurrentHashMap.newKeySet();
static boolean isOk(PolyStringType name, CacheConfigurationManager cacheConfigurationManager) {
@@ -296,6 +297,13 @@ protected int getSize() {
return conflictFreeNames.size();
}
+ @Override
+ protected void dumpContent(String threadName) {
+ if (LOGGER_CONTENT.isInfoEnabled()) {
+ conflictFreeNames.forEach(name -> LOGGER_CONTENT.info("Cached conflict-free name [{}]: {}", threadName, name));
+ }
+ }
+
private static void log(String message, boolean info, Object... params) {
CacheUtil.log(LOGGER, PERFORMANCE_ADVISOR, message, info, params);
}
diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java
index 8b22a3b285e..f24b1fc49a4 100644
--- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java
+++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java
@@ -43,10 +43,8 @@
import javax.xml.namespace.QName;
import java.util.Collection;
import java.util.List;
-import java.util.Map;
import static com.evolveum.midpoint.model.impl.scripting.VariablesUtil.cloneIfNecessary;
-import static com.evolveum.midpoint.schema.constants.SchemaConstants.NS_C;
/**
* @author mederly
@@ -87,7 +85,8 @@ public PipelineData execute(ActionExpressionType expression, PipelineData input,
ScriptExpression scriptExpression;
try {
- scriptExpression = scriptExpressionFactory.createScriptExpression(script, outputDefinition, expressionProfile, expressionFactory, "script", context.getTask(), globalResult);
+ scriptExpression = scriptExpressionFactory.createScriptExpression(script, outputDefinition, expressionProfile, expressionFactory, "script",
+ globalResult);
} catch (ExpressionSyntaxException | SecurityViolationException e) {
throw new ScriptExecutionException("Couldn't parse script expression: " + e.getMessage(), e);
}
diff --git a/model/model-impl/src/main/resources/ctx-model.xml b/model/model-impl/src/main/resources/ctx-model.xml
index fe9c2343717..2ae944e9fd9 100644
--- a/model/model-impl/src/main/resources/ctx-model.xml
+++ b/model/model-impl/src/main/resources/ctx-model.xml
@@ -94,7 +94,6 @@
-
diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/expr/TestModelExpressions.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/expr/TestModelExpressions.java
index 8d862315721..e85c441a00a 100644
--- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/expr/TestModelExpressions.java
+++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/expr/TestModelExpressions.java
@@ -136,7 +136,7 @@ public void testGetManagersOids() throws Exception {
PROPERTY_NAME, DOMUtil.XSD_STRING);
ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(
scriptType, outputDefinition, MiscSchemaUtil.getExpressionProfile(),
- expressionFactory, shortTestName, task, result);
+ expressionFactory, shortTestName, result);
ExpressionVariables variables =
createVariables(ExpressionConstants.VAR_USER, chef, chef.getDefinition());
@@ -171,7 +171,7 @@ public void testIsUniquePropertyValue() throws Exception {
ScriptExpressionEvaluatorType scriptType = parseScriptType("expression-" + testName + ".xml");
PrismPropertyDefinition outputDefinition = getPrismContext().definitionFactory().createPropertyDefinition(PROPERTY_NAME, DOMUtil.XSD_BOOLEAN);
ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(scriptType, outputDefinition,
- MiscSchemaUtil.getExpressionProfile(), expressionFactory, testName, task, result);
+ MiscSchemaUtil.getExpressionProfile(), expressionFactory, testName, result);
ExpressionVariables variables = createVariables(
ExpressionConstants.VAR_USER, chef, chef.getDefinition(),
@@ -292,7 +292,7 @@ private String executeScriptExpressionString(ExpressionVariables variables)
PROPERTY_NAME, DOMUtil.XSD_STRING);
ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(
scriptType, outputDefinition, MiscSchemaUtil.getExpressionProfile(),
- expressionFactory, shortTestName, task, result);
+ expressionFactory, shortTestName, result);
if (variables == null) {
variables = new ExpressionVariables();
}
diff --git a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportServiceImpl.java b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportServiceImpl.java
index 8252ad38d67..f8459141719 100644
--- a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportServiceImpl.java
+++ b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportServiceImpl.java
@@ -1,440 +1,441 @@
-/*
- * Copyright (c) 2010-2019 Evolveum and contributors
- *
- * This work is dual-licensed under the Apache License 2.0
- * and European Union Public License. See LICENSE file for details.
- */
-package com.evolveum.midpoint.report.impl;
-
-import java.util.*;
-import java.util.Map.Entry;
-import javax.xml.namespace.QName;
-
-import org.apache.commons.lang.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Component;
-
-import com.evolveum.midpoint.audit.api.AuditEventRecord;
-import com.evolveum.midpoint.audit.api.AuditService;
-import com.evolveum.midpoint.model.api.ModelAuthorizationAction;
-import com.evolveum.midpoint.model.api.ModelService;
-import com.evolveum.midpoint.model.common.ArchetypeManager;
-import com.evolveum.midpoint.model.common.expression.ExpressionEnvironment;
-import com.evolveum.midpoint.model.common.expression.ModelExpressionThreadLocalHolder;
-import com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary;
-import com.evolveum.midpoint.model.common.expression.script.ScriptExpression;
-import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluationContext;
-import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluatorFactory;
-import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionFactory;
-import com.evolveum.midpoint.model.common.expression.script.groovy.GroovyScriptEvaluator;
-import com.evolveum.midpoint.prism.*;
-import com.evolveum.midpoint.prism.query.ObjectFilter;
-import com.evolveum.midpoint.prism.query.ObjectQuery;
-import com.evolveum.midpoint.prism.query.TypeFilter;
-import com.evolveum.midpoint.repo.common.ObjectResolver;
-import com.evolveum.midpoint.repo.common.expression.ExpressionFactory;
-import com.evolveum.midpoint.repo.common.expression.ExpressionUtil;
-import com.evolveum.midpoint.repo.common.expression.ExpressionVariables;
-import com.evolveum.midpoint.report.api.ReportService;
-import com.evolveum.midpoint.schema.GetOperationOptions;
-import com.evolveum.midpoint.schema.SchemaHelper;
-import com.evolveum.midpoint.schema.SelectorOptions;
-import com.evolveum.midpoint.schema.expression.*;
-import com.evolveum.midpoint.schema.result.OperationResult;
-import com.evolveum.midpoint.schema.util.ObjectQueryUtil;
-import com.evolveum.midpoint.security.enforcer.api.AuthorizationParameters;
-import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer;
-import com.evolveum.midpoint.task.api.Task;
-import com.evolveum.midpoint.task.api.TaskManager;
-import com.evolveum.midpoint.util.exception.*;
-import com.evolveum.midpoint.util.logging.Trace;
-import com.evolveum.midpoint.util.logging.TraceManager;
-import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
-import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType;
-
-@Component
-public class ReportServiceImpl implements ReportService {
-
- private static final Trace LOGGER = TraceManager.getTrace(ReportServiceImpl.class);
-
- @Autowired private ModelService model;
- @Autowired private TaskManager taskManager;
- @Autowired private PrismContext prismContext;
- @Autowired private SchemaHelper schemaHelper;
- @Autowired private ExpressionFactory expressionFactory;
- @Autowired @Qualifier("modelObjectResolver") private ObjectResolver objectResolver;
- @Autowired private AuditService auditService;
- @Autowired private FunctionLibrary logFunctionLibrary;
- @Autowired private FunctionLibrary basicFunctionLibrary;
- @Autowired private FunctionLibrary midpointFunctionLibrary;
- @Autowired private SecurityEnforcer securityEnforcer;
- @Autowired private ScriptExpressionFactory scriptExpressionFactory;
- @Autowired private ArchetypeManager archetypeManager;
-
- @Override
- public ObjectQuery parseQuery(PrismObject report, String query, VariablesMap parameters, Task task, OperationResult result) throws SchemaException,
- ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
- if (StringUtils.isBlank(query)) {
- return null;
- }
-
- ObjectQuery parsedQuery;
- try {
-
- ExpressionProfile expressionProfile = determineExpressionProfile(report, result);
-
- ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(task, result));
- SearchFilterType filterType = prismContext.parserFor(query).parseRealValue(SearchFilterType.class);
- if (LOGGER.isTraceEnabled()) {
- LOGGER.trace("filter(SearchFilterType)\n{}", filterType.debugDump(1));
- }
- ObjectFilter filter = prismContext.getQueryConverter().parseFilter(filterType, UserType.class);
- if (LOGGER.isTraceEnabled()) {
- LOGGER.trace("filter(ObjectFilter)\n{}", filter.debugDump(1));
- }
- if (!(filter instanceof TypeFilter)) {
- throw new IllegalArgumentException(
- "Defined query must contain type. Use 'type filter' in your report query.");
- }
-
- ObjectFilter subFilter = ((TypeFilter) filter).getFilter();
- ObjectQuery q = prismContext.queryFactory().createQuery(subFilter);
-
- ExpressionVariables variables = new ExpressionVariables();
- variables.putAll(parameters);
-
- q = ExpressionUtil.evaluateQueryExpressions(q, variables, expressionProfile, expressionFactory, prismContext,
- "parsing expression values for report", task, result);
- ((TypeFilter) filter).setFilter(q.getFilter());
- ObjectQueryUtil.simplify(filter, prismContext);
- parsedQuery = prismContext.queryFactory().createQuery(filter);
-
- if (LOGGER.isTraceEnabled()) {
- LOGGER.trace("report query (parsed):\n{}", parsedQuery.debugDump(1));
- }
- } catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException
- | CommunicationException | ConfigurationException | SecurityViolationException e) {
- LOGGER.error("Cannot convert query, reason: {}", e.getMessage());
- throw e;
- } finally {
- ModelExpressionThreadLocalHolder.popExpressionEnvironment();
- }
- return parsedQuery;
-
- }
-
- @Override
- public Collection> searchObjects(ObjectQuery query, Collection> options, Task task, OperationResult parentResult)
- throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
- // List> results = new ArrayList<>();
-
- // GetOperationOptions options = GetOperationOptions.createRaw();
-
- if (!(query.getFilter() instanceof TypeFilter)) {
- throw new IllegalArgumentException("Query must contain type filter.");
- }
-
- TypeFilter typeFilter = (TypeFilter) query.getFilter();
- QName type = typeFilter.getType();
- Class clazz = prismContext.getSchemaRegistry().determineCompileTimeClass(type);
- if (clazz == null) {
- PrismObjectDefinition objectDef = prismContext.getSchemaRegistry().findObjectDefinitionByType(type);
- if (objectDef == null) {
- throw new SchemaException("Undefined object type used in query, type: " + type);
- }
- clazz = objectDef.getCompileTimeClass();
- }
-
- ObjectQuery queryForSearch = prismContext.queryFactory().createQuery(typeFilter.getFilter());
-
- // options.add(new
- // SelectorOptions(GetOperationOptions.createResolveNames()));
- GetOperationOptions getOptions = GetOperationOptions.createResolveNames();
- if (ShadowType.class.isAssignableFrom(clazz) && securityEnforcer.isAuthorized(ModelAuthorizationAction.RAW_OPERATION.getUrl(), null, AuthorizationParameters.EMPTY, null, task, parentResult)) {
- LOGGER.trace("Setting searching in raw mode.");
- getOptions.setRaw(Boolean.TRUE); // shadows in non-raw mode require specifying resource OID and kind (at least) - todo research this further
- } else {
- LOGGER.trace("Setting searching in noFetch mode. Shadows in non-raw mode require specifying resource OID and objectClass (kind) at least.");
- getOptions.setNoFetch(Boolean.TRUE);
- }
- options = SelectorOptions.createCollection(getOptions);
- List> results;
- try {
- results = model.searchObjects(clazz, queryForSearch, options, task, parentResult);
- return results;
- } catch (SchemaException | ObjectNotFoundException | SecurityViolationException
- | CommunicationException | ConfigurationException | ExpressionEvaluationException e) {
- // TODO Auto-generated catch block
- throw e;
- }
-
- }
-
- @Override
- public Collection> evaluateScript(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result)
- throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException {
-
- ExpressionVariables variables = new ExpressionVariables();
- variables.putAll(parameters);
-
- TypedValue auditParams = getConvertedParams(parameters);
- // special variable for audit report
- variables.put("auditParams", auditParams);
-
- ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext();
- context.setVariables(variables);
- context.setContextDescription("report script"); // TODO: improve
- context.setTask(task);
- context.setResult(result);
- setupExpressionProfiles(context, report);
-
- Object o = evaluateReportScript(script, context, report);
-
- List> results = new ArrayList<>();
- if (o != null) {
-
- if (Collection.class.isAssignableFrom(o.getClass())) {
- Collection resultSet = (Collection) o;
- if (resultSet != null && !resultSet.isEmpty()) {
- for (Object obj : resultSet) {
- results.add(convertResultingObject(obj));
- }
- }
-
- } else {
- results.add(convertResultingObject(o));
- }
- }
-
- return results;
- }
-
-
- private Collection runAuditQuery(String sqlWhereClause, TypedValue jasperAuditParams, OperationResult result) {
- if (StringUtils.isBlank(sqlWhereClause)) {
- return new ArrayList<>();
- }
-
- String query = "select * from m_audit_event as aer " + sqlWhereClause;
- LOGGER.trace("AAAAAAA: query: {}", query);
- Map auditParams = ReportUtils.jasperParamsToAuditParams((VariablesMap)jasperAuditParams.getValue());
- LOGGER.trace("AAAAAAA: auditParams:\n{}", auditParams);
- List auditRecords = auditService.listRecords(query, auditParams, result);
- LOGGER.trace("AAAAAAA: {} records", auditRecords==null?null:auditRecords.size());
- return auditRecords;
- }
-
- @Override
- public Object evaluate(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException,
- ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException {
-
- ExpressionVariables variables = new ExpressionVariables();
- variables.addVariableDefinitions(parameters);
-
- // special variable for audit report
- variables.put("auditParams", getConvertedParams(parameters));
-
- ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext();
- context.setVariables(variables);
- context.setContextDescription("report script"); // TODO: improve
- context.setTask(task);
- context.setResult(result);
- setupExpressionProfiles(context, report);
-
- Object o = evaluateReportScript(script, context, report);
-
- return o;
-
- }
-
- protected PrismContainerValue convertResultingObject(Object obj) {
- if (obj instanceof PrismObject) {
- return ((PrismObject) obj).asObjectable().asPrismContainerValue();
- } else if (obj instanceof Objectable) {
- return ((Objectable) obj).asPrismContainerValue();
- } else if (obj instanceof PrismContainerValue) {
- return (PrismContainerValue) obj;
- } else if (obj instanceof Containerable) {
- return ((Containerable) obj).asPrismContainerValue();
- } else {
- throw new IllegalStateException("Reporting script should return something compatible with PrismContainerValue, not a " + obj.getClass());
- }
- }
-
- @Override
- public Collection evaluateAuditScript(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result)
- throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException {
- Collection results = new ArrayList<>();
-
- TypedValue auditParams = getConvertedParams(parameters);
- ExpressionVariables variables = new ExpressionVariables();
- variables.put("auditParams", auditParams);
-
- ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext();
- context.setVariables(variables);
-
- context.setContextDescription("report script"); // TODO: improve
- context.setTask(task);
- context.setResult(result);
- setupExpressionProfiles(context, report);
-
- Object o = evaluateReportScript(script, context, report);
-
- // HACK to allow audit reports where query is just a plain string.
- // Oh my, this code is a mess. This needs a real rewrite. MID-5572
- if (o instanceof String) {
- JasperReportEngineConfigurationType jasperConfig = report.asObjectable().getJasper();
- if (jasperConfig == null) {
- throw new SchemaException("Jasper reportType not set, cannot determine how to use string query");
- }
- JasperReportTypeType reportType = jasperConfig.getReportType();
- if (reportType == null) {
- throw new SchemaException("Jasper reportType not set, cannot determine how to use string query");
- }
- if (reportType.equals(JasperReportTypeType.AUDIT_SQL)) {
- return runAuditQuery((String)o, auditParams, result);
- } else {
- throw new SchemaException("Jasper reportType is not set to auditSql, cannot determine how to use string query");
- }
- }
-
- if (o != null) {
-
- if (Collection.class.isAssignableFrom(o.getClass())) {
- Collection resultSet = (Collection) o;
- if (resultSet != null && !resultSet.isEmpty()) {
- for (Object obj : resultSet) {
- if (!(obj instanceof AuditEventRecord)) {
- LOGGER.warn("Skipping result, not an audit event record " + obj);
- continue;
- }
- results.add((AuditEventRecord) obj);
- }
-
- }
-
- } else {
- results.add((AuditEventRecord) o);
- }
- }
-
- return results;
- }
-
- private TypedValue getConvertedParams(VariablesMap parameters) {
- if (parameters == null) {
- return new TypedValue<>(null, VariablesMap.class);
- }
-
- VariablesMap resultParamMap = new VariablesMap();
- Set> paramEntries = parameters.entrySet();
- for (Entry e : paramEntries) {
- Object value = e.getValue().getValue();
- if (value instanceof PrismPropertyValue) {
- resultParamMap.put(e.getKey(), e.getValue().createTransformed(((PrismPropertyValue) value).getValue()));
- } else {
- resultParamMap.put(e.getKey(), e.getValue());
- }
- }
-
- return new TypedValue<>(resultParamMap, VariablesMap.class);
- }
-
- private Collection createFunctionLibraries() {
- FunctionLibrary midPointLib = new FunctionLibrary();
- midPointLib.setVariableName("report");
- midPointLib.setNamespace("http://midpoint.evolveum.com/xml/ns/public/function/report-3");
- ReportFunctions reportFunctions = new ReportFunctions(prismContext, schemaHelper, model, taskManager, auditService);
- midPointLib.setGenericFunctions(reportFunctions);
-
- Collection functions = new ArrayList<>();
- functions.add(basicFunctionLibrary);
- functions.add(logFunctionLibrary);
- functions.add(midpointFunctionLibrary);
- functions.add(midPointLib);
- return functions;
- }
-
- @Override
- public PrismContext getPrismContext() {
- return prismContext;
- }
-
- public Object evaluateReportScript(String codeString, ScriptExpressionEvaluationContext context, PrismObject report) throws ExpressionEvaluationException,
- ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, SchemaException {
-
- ScriptExpressionEvaluatorConfigurationType defaultScriptConfiguration = report.asObjectable().getDefaultScriptConfiguration();
- ScriptExpressionEvaluatorType expressionType = new ScriptExpressionEvaluatorType();
- expressionType.setCode(codeString);
- expressionType.setObjectVariableMode(defaultScriptConfiguration == null ? ObjectVariableModeType.OBJECT : defaultScriptConfiguration.getObjectVariableMode());
- context.setExpressionType(expressionType);
- // Be careful about output definition here. We really do NOT want to set it.
- // Not setting the definition means that we want raw value without any conversion.
- // This is what we really want, because there may be exotic things such as JRTemplate going through those expressions
- // We do not have any reasonable prism definitions for those.
-
- context.setFunctions(createFunctionLibraries());
- context.setObjectResolver(objectResolver);
-
- ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(
- expressionType, context.getOutputDefinition(), context.getExpressionProfile(), expressionFactory, context.getContextDescription(), context.getTask(), context.getResult());
-
- ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(context.getTask(), context.getResult()));
- List expressionResult;
- try {
- expressionResult = scriptExpression.evaluate(context);
- } finally {
- ModelExpressionThreadLocalHolder.popExpressionEnvironment();
- }
-
- if (expressionResult == null || expressionResult.isEmpty()) {
- return null;
- }
- if (expressionResult.size() > 1) {
- throw new ExpressionEvaluationException("Too many results from expression "+context.getContextDescription());
- }
- return expressionResult.get(0).getRealValue();
- }
-
- private ExpressionProfile determineExpressionProfile(PrismObject report, OperationResult result) throws SchemaException, ConfigurationException {
- if (report == null) {
- throw new IllegalArgumentException("No report defined, cannot determine profile");
- }
- return archetypeManager.determineExpressionProfile(report, result);
- }
-
- private void setupExpressionProfiles(ScriptExpressionEvaluationContext context, PrismObject report) throws SchemaException, ConfigurationException {
- ExpressionProfile expressionProfile = determineExpressionProfile(report, context.getResult());
- LOGGER.trace("Using expression profile '"+(expressionProfile==null?null:expressionProfile.getIdentifier())+"' for report evaluation, determined from: {}", report);
- context.setExpressionProfile(expressionProfile);
- context.setScriptExpressionProfile(findScriptExpressionProfile(expressionProfile, report));
- }
-
- private ScriptExpressionProfile findScriptExpressionProfile(ExpressionProfile expressionProfile, PrismObject report) {
- if (expressionProfile == null) {
- return null;
- }
- ExpressionEvaluatorProfile scriptEvaluatorProfile = expressionProfile.getEvaluatorProfile(ScriptExpressionEvaluatorFactory.ELEMENT_NAME);
- if (scriptEvaluatorProfile == null) {
- return null;
- }
- return scriptEvaluatorProfile.getScriptExpressionProfile(getScriptLanguageName(report));
- }
-
- private String getScriptLanguageName(PrismObject report) {
- // Hardcoded for now
- return GroovyScriptEvaluator.LANGUAGE_NAME;
- }
-
- @Override
- public PrismObject getReportDefinition(String reportOid, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
- return model.getObject(ReportType.class, reportOid, null, task, result);
- }
-
- @Override
- public boolean isAuthorizedToRunReport(PrismObject report, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
- AuthorizationParameters params = AuthorizationParameters.Builder.buildObject(report);
- return securityEnforcer.isAuthorized(ModelAuthorizationAction.RUN_REPORT.getUrl(), null, params, null, task, result);
- }
-}
+/*
+ * Copyright (c) 2010-2019 Evolveum and contributors
+ *
+ * This work is dual-licensed under the Apache License 2.0
+ * and European Union Public License. See LICENSE file for details.
+ */
+package com.evolveum.midpoint.report.impl;
+
+import java.util.*;
+import java.util.Map.Entry;
+import javax.xml.namespace.QName;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import com.evolveum.midpoint.audit.api.AuditEventRecord;
+import com.evolveum.midpoint.audit.api.AuditService;
+import com.evolveum.midpoint.model.api.ModelAuthorizationAction;
+import com.evolveum.midpoint.model.api.ModelService;
+import com.evolveum.midpoint.model.common.ArchetypeManager;
+import com.evolveum.midpoint.model.common.expression.ExpressionEnvironment;
+import com.evolveum.midpoint.model.common.expression.ModelExpressionThreadLocalHolder;
+import com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary;
+import com.evolveum.midpoint.model.common.expression.script.ScriptExpression;
+import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluationContext;
+import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluatorFactory;
+import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionFactory;
+import com.evolveum.midpoint.model.common.expression.script.groovy.GroovyScriptEvaluator;
+import com.evolveum.midpoint.prism.*;
+import com.evolveum.midpoint.prism.query.ObjectFilter;
+import com.evolveum.midpoint.prism.query.ObjectQuery;
+import com.evolveum.midpoint.prism.query.TypeFilter;
+import com.evolveum.midpoint.repo.common.ObjectResolver;
+import com.evolveum.midpoint.repo.common.expression.ExpressionFactory;
+import com.evolveum.midpoint.repo.common.expression.ExpressionUtil;
+import com.evolveum.midpoint.repo.common.expression.ExpressionVariables;
+import com.evolveum.midpoint.report.api.ReportService;
+import com.evolveum.midpoint.schema.GetOperationOptions;
+import com.evolveum.midpoint.schema.SchemaHelper;
+import com.evolveum.midpoint.schema.SelectorOptions;
+import com.evolveum.midpoint.schema.expression.*;
+import com.evolveum.midpoint.schema.result.OperationResult;
+import com.evolveum.midpoint.schema.util.ObjectQueryUtil;
+import com.evolveum.midpoint.security.enforcer.api.AuthorizationParameters;
+import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer;
+import com.evolveum.midpoint.task.api.Task;
+import com.evolveum.midpoint.task.api.TaskManager;
+import com.evolveum.midpoint.util.exception.*;
+import com.evolveum.midpoint.util.logging.Trace;
+import com.evolveum.midpoint.util.logging.TraceManager;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
+import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType;
+
+@Component
+public class ReportServiceImpl implements ReportService {
+
+ private static final Trace LOGGER = TraceManager.getTrace(ReportServiceImpl.class);
+
+ @Autowired private ModelService model;
+ @Autowired private TaskManager taskManager;
+ @Autowired private PrismContext prismContext;
+ @Autowired private SchemaHelper schemaHelper;
+ @Autowired private ExpressionFactory expressionFactory;
+ @Autowired @Qualifier("modelObjectResolver") private ObjectResolver objectResolver;
+ @Autowired private AuditService auditService;
+ @Autowired private FunctionLibrary logFunctionLibrary;
+ @Autowired private FunctionLibrary basicFunctionLibrary;
+ @Autowired private FunctionLibrary midpointFunctionLibrary;
+ @Autowired private SecurityEnforcer securityEnforcer;
+ @Autowired private ScriptExpressionFactory scriptExpressionFactory;
+ @Autowired private ArchetypeManager archetypeManager;
+
+ @Override
+ public ObjectQuery parseQuery(PrismObject report, String query, VariablesMap parameters, Task task, OperationResult result) throws SchemaException,
+ ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
+ if (StringUtils.isBlank(query)) {
+ return null;
+ }
+
+ ObjectQuery parsedQuery;
+ try {
+
+ ExpressionProfile expressionProfile = determineExpressionProfile(report, result);
+
+ ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(task, result));
+ SearchFilterType filterType = prismContext.parserFor(query).parseRealValue(SearchFilterType.class);
+ if (LOGGER.isTraceEnabled()) {
+ LOGGER.trace("filter(SearchFilterType)\n{}", filterType.debugDump(1));
+ }
+ ObjectFilter filter = prismContext.getQueryConverter().parseFilter(filterType, UserType.class);
+ if (LOGGER.isTraceEnabled()) {
+ LOGGER.trace("filter(ObjectFilter)\n{}", filter.debugDump(1));
+ }
+ if (!(filter instanceof TypeFilter)) {
+ throw new IllegalArgumentException(
+ "Defined query must contain type. Use 'type filter' in your report query.");
+ }
+
+ ObjectFilter subFilter = ((TypeFilter) filter).getFilter();
+ ObjectQuery q = prismContext.queryFactory().createQuery(subFilter);
+
+ ExpressionVariables variables = new ExpressionVariables();
+ variables.putAll(parameters);
+
+ q = ExpressionUtil.evaluateQueryExpressions(q, variables, expressionProfile, expressionFactory, prismContext,
+ "parsing expression values for report", task, result);
+ ((TypeFilter) filter).setFilter(q.getFilter());
+ ObjectQueryUtil.simplify(filter, prismContext);
+ parsedQuery = prismContext.queryFactory().createQuery(filter);
+
+ if (LOGGER.isTraceEnabled()) {
+ LOGGER.trace("report query (parsed):\n{}", parsedQuery.debugDump(1));
+ }
+ } catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException
+ | CommunicationException | ConfigurationException | SecurityViolationException e) {
+ LOGGER.error("Cannot convert query, reason: {}", e.getMessage());
+ throw e;
+ } finally {
+ ModelExpressionThreadLocalHolder.popExpressionEnvironment();
+ }
+ return parsedQuery;
+
+ }
+
+ @Override
+ public Collection> searchObjects(ObjectQuery query, Collection> options, Task task, OperationResult parentResult)
+ throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
+ // List> results = new ArrayList<>();
+
+ // GetOperationOptions options = GetOperationOptions.createRaw();
+
+ if (!(query.getFilter() instanceof TypeFilter)) {
+ throw new IllegalArgumentException("Query must contain type filter.");
+ }
+
+ TypeFilter typeFilter = (TypeFilter) query.getFilter();
+ QName type = typeFilter.getType();
+ Class clazz = prismContext.getSchemaRegistry().determineCompileTimeClass(type);
+ if (clazz == null) {
+ PrismObjectDefinition objectDef = prismContext.getSchemaRegistry().findObjectDefinitionByType(type);
+ if (objectDef == null) {
+ throw new SchemaException("Undefined object type used in query, type: " + type);
+ }
+ clazz = objectDef.getCompileTimeClass();
+ }
+
+ ObjectQuery queryForSearch = prismContext.queryFactory().createQuery(typeFilter.getFilter());
+
+ // options.add(new
+ // SelectorOptions(GetOperationOptions.createResolveNames()));
+ GetOperationOptions getOptions = GetOperationOptions.createResolveNames();
+ if (ShadowType.class.isAssignableFrom(clazz) && securityEnforcer.isAuthorized(ModelAuthorizationAction.RAW_OPERATION.getUrl(), null, AuthorizationParameters.EMPTY, null, task, parentResult)) {
+ LOGGER.trace("Setting searching in raw mode.");
+ getOptions.setRaw(Boolean.TRUE); // shadows in non-raw mode require specifying resource OID and kind (at least) - todo research this further
+ } else {
+ LOGGER.trace("Setting searching in noFetch mode. Shadows in non-raw mode require specifying resource OID and objectClass (kind) at least.");
+ getOptions.setNoFetch(Boolean.TRUE);
+ }
+ options = SelectorOptions.createCollection(getOptions);
+ List> results;
+ try {
+ results = model.searchObjects(clazz, queryForSearch, options, task, parentResult);
+ return results;
+ } catch (SchemaException | ObjectNotFoundException | SecurityViolationException
+ | CommunicationException | ConfigurationException | ExpressionEvaluationException e) {
+ // TODO Auto-generated catch block
+ throw e;
+ }
+
+ }
+
+ @Override
+ public Collection> evaluateScript(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result)
+ throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException {
+
+ ExpressionVariables variables = new ExpressionVariables();
+ variables.putAll(parameters);
+
+ TypedValue auditParams = getConvertedParams(parameters);
+ // special variable for audit report
+ variables.put("auditParams", auditParams);
+
+ ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext();
+ context.setVariables(variables);
+ context.setContextDescription("report script"); // TODO: improve
+ context.setTask(task);
+ context.setResult(result);
+ setupExpressionProfiles(context, report);
+
+ Object o = evaluateReportScript(script, context, report);
+
+ List> results = new ArrayList<>();
+ if (o != null) {
+
+ if (Collection.class.isAssignableFrom(o.getClass())) {
+ Collection resultSet = (Collection) o;
+ if (resultSet != null && !resultSet.isEmpty()) {
+ for (Object obj : resultSet) {
+ results.add(convertResultingObject(obj));
+ }
+ }
+
+ } else {
+ results.add(convertResultingObject(o));
+ }
+ }
+
+ return results;
+ }
+
+
+ private Collection runAuditQuery(String sqlWhereClause, TypedValue jasperAuditParams, OperationResult result) {
+ if (StringUtils.isBlank(sqlWhereClause)) {
+ return new ArrayList<>();
+ }
+
+ String query = "select * from m_audit_event as aer " + sqlWhereClause;
+ LOGGER.trace("AAAAAAA: query: {}", query);
+ Map auditParams = ReportUtils.jasperParamsToAuditParams((VariablesMap)jasperAuditParams.getValue());
+ LOGGER.trace("AAAAAAA: auditParams:\n{}", auditParams);
+ List auditRecords = auditService.listRecords(query, auditParams, result);
+ LOGGER.trace("AAAAAAA: {} records", auditRecords==null?null:auditRecords.size());
+ return auditRecords;
+ }
+
+ @Override
+ public Object evaluate(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException,
+ ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException {
+
+ ExpressionVariables variables = new ExpressionVariables();
+ variables.addVariableDefinitions(parameters);
+
+ // special variable for audit report
+ variables.put("auditParams", getConvertedParams(parameters));
+
+ ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext();
+ context.setVariables(variables);
+ context.setContextDescription("report script"); // TODO: improve
+ context.setTask(task);
+ context.setResult(result);
+ setupExpressionProfiles(context, report);
+
+ Object o = evaluateReportScript(script, context, report);
+
+ return o;
+
+ }
+
+ protected PrismContainerValue convertResultingObject(Object obj) {
+ if (obj instanceof PrismObject) {
+ return ((PrismObject) obj).asObjectable().asPrismContainerValue();
+ } else if (obj instanceof Objectable) {
+ return ((Objectable) obj).asPrismContainerValue();
+ } else if (obj instanceof PrismContainerValue) {
+ return (PrismContainerValue) obj;
+ } else if (obj instanceof Containerable) {
+ return ((Containerable) obj).asPrismContainerValue();
+ } else {
+ throw new IllegalStateException("Reporting script should return something compatible with PrismContainerValue, not a " + obj.getClass());
+ }
+ }
+
+ @Override
+ public Collection evaluateAuditScript(PrismObject report, String script, VariablesMap parameters, Task task, OperationResult result)
+ throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException {
+ Collection results = new ArrayList<>();
+
+ TypedValue auditParams = getConvertedParams(parameters);
+ ExpressionVariables variables = new ExpressionVariables();
+ variables.put("auditParams", auditParams);
+
+ ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext();
+ context.setVariables(variables);
+
+ context.setContextDescription("report script"); // TODO: improve
+ context.setTask(task);
+ context.setResult(result);
+ setupExpressionProfiles(context, report);
+
+ Object o = evaluateReportScript(script, context, report);
+
+ // HACK to allow audit reports where query is just a plain string.
+ // Oh my, this code is a mess. This needs a real rewrite. MID-5572
+ if (o instanceof String) {
+ JasperReportEngineConfigurationType jasperConfig = report.asObjectable().getJasper();
+ if (jasperConfig == null) {
+ throw new SchemaException("Jasper reportType not set, cannot determine how to use string query");
+ }
+ JasperReportTypeType reportType = jasperConfig.getReportType();
+ if (reportType == null) {
+ throw new SchemaException("Jasper reportType not set, cannot determine how to use string query");
+ }
+ if (reportType.equals(JasperReportTypeType.AUDIT_SQL)) {
+ return runAuditQuery((String)o, auditParams, result);
+ } else {
+ throw new SchemaException("Jasper reportType is not set to auditSql, cannot determine how to use string query");
+ }
+ }
+
+ if (o != null) {
+
+ if (Collection.class.isAssignableFrom(o.getClass())) {
+ Collection resultSet = (Collection) o;
+ if (resultSet != null && !resultSet.isEmpty()) {
+ for (Object obj : resultSet) {
+ if (!(obj instanceof AuditEventRecord)) {
+ LOGGER.warn("Skipping result, not an audit event record " + obj);
+ continue;
+ }
+ results.add((AuditEventRecord) obj);
+ }
+
+ }
+
+ } else {
+ results.add((AuditEventRecord) o);
+ }
+ }
+
+ return results;
+ }
+
+ private TypedValue getConvertedParams(VariablesMap parameters) {
+ if (parameters == null) {
+ return new TypedValue<>(null, VariablesMap.class);
+ }
+
+ VariablesMap resultParamMap = new VariablesMap();
+ Set> paramEntries = parameters.entrySet();
+ for (Entry e : paramEntries) {
+ Object value = e.getValue().getValue();
+ if (value instanceof PrismPropertyValue) {
+ resultParamMap.put(e.getKey(), e.getValue().createTransformed(((PrismPropertyValue) value).getValue()));
+ } else {
+ resultParamMap.put(e.getKey(), e.getValue());
+ }
+ }
+
+ return new TypedValue<>(resultParamMap, VariablesMap.class);
+ }
+
+ private Collection createFunctionLibraries() {
+ FunctionLibrary midPointLib = new FunctionLibrary();
+ midPointLib.setVariableName("report");
+ midPointLib.setNamespace("http://midpoint.evolveum.com/xml/ns/public/function/report-3");
+ ReportFunctions reportFunctions = new ReportFunctions(prismContext, schemaHelper, model, taskManager, auditService);
+ midPointLib.setGenericFunctions(reportFunctions);
+
+ Collection functions = new ArrayList<>();
+ functions.add(basicFunctionLibrary);
+ functions.add(logFunctionLibrary);
+ functions.add(midpointFunctionLibrary);
+ functions.add(midPointLib);
+ return functions;
+ }
+
+ @Override
+ public PrismContext getPrismContext() {
+ return prismContext;
+ }
+
+ public Object evaluateReportScript(String codeString, ScriptExpressionEvaluationContext context, PrismObject report) throws ExpressionEvaluationException,
+ ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, SchemaException {
+
+ ScriptExpressionEvaluatorConfigurationType defaultScriptConfiguration = report.asObjectable().getDefaultScriptConfiguration();
+ ScriptExpressionEvaluatorType expressionType = new ScriptExpressionEvaluatorType();
+ expressionType.setCode(codeString);
+ expressionType.setObjectVariableMode(defaultScriptConfiguration == null ? ObjectVariableModeType.OBJECT : defaultScriptConfiguration.getObjectVariableMode());
+ context.setExpressionType(expressionType);
+ // Be careful about output definition here. We really do NOT want to set it.
+ // Not setting the definition means that we want raw value without any conversion.
+ // This is what we really want, because there may be exotic things such as JRTemplate going through those expressions
+ // We do not have any reasonable prism definitions for those.
+
+ context.setFunctions(createFunctionLibraries());
+ context.setObjectResolver(objectResolver);
+
+ ScriptExpression scriptExpression = scriptExpressionFactory.createScriptExpression(
+ expressionType, context.getOutputDefinition(), context.getExpressionProfile(), expressionFactory, context.getContextDescription(),
+ context.getResult());
+
+ ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(context.getTask(), context.getResult()));
+ List expressionResult;
+ try {
+ expressionResult = scriptExpression.evaluate(context);
+ } finally {
+ ModelExpressionThreadLocalHolder.popExpressionEnvironment();
+ }
+
+ if (expressionResult == null || expressionResult.isEmpty()) {
+ return null;
+ }
+ if (expressionResult.size() > 1) {
+ throw new ExpressionEvaluationException("Too many results from expression "+context.getContextDescription());
+ }
+ return expressionResult.get(0).getRealValue();
+ }
+
+ private ExpressionProfile determineExpressionProfile(PrismObject report, OperationResult result) throws SchemaException, ConfigurationException {
+ if (report == null) {
+ throw new IllegalArgumentException("No report defined, cannot determine profile");
+ }
+ return archetypeManager.determineExpressionProfile(report, result);
+ }
+
+ private void setupExpressionProfiles(ScriptExpressionEvaluationContext context, PrismObject report) throws SchemaException, ConfigurationException {
+ ExpressionProfile expressionProfile = determineExpressionProfile(report, context.getResult());
+ LOGGER.trace("Using expression profile '"+(expressionProfile==null?null:expressionProfile.getIdentifier())+"' for report evaluation, determined from: {}", report);
+ context.setExpressionProfile(expressionProfile);
+ context.setScriptExpressionProfile(findScriptExpressionProfile(expressionProfile, report));
+ }
+
+ private ScriptExpressionProfile findScriptExpressionProfile(ExpressionProfile expressionProfile, PrismObject report) {
+ if (expressionProfile == null) {
+ return null;
+ }
+ ExpressionEvaluatorProfile scriptEvaluatorProfile = expressionProfile.getEvaluatorProfile(ScriptExpressionEvaluatorFactory.ELEMENT_NAME);
+ if (scriptEvaluatorProfile == null) {
+ return null;
+ }
+ return scriptEvaluatorProfile.getScriptExpressionProfile(getScriptLanguageName(report));
+ }
+
+ private String getScriptLanguageName(PrismObject report) {
+ // Hardcoded for now
+ return GroovyScriptEvaluator.LANGUAGE_NAME;
+ }
+
+ @Override
+ public PrismObject getReportDefinition(String reportOid, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
+ return model.getObject(ReportType.class, reportOid, null, task, result);
+ }
+
+ @Override
+ public boolean isAuthorizedToRunReport(PrismObject report, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
+ AuthorizationParameters params = AuthorizationParameters.Builder.buildObject(report);
+ return securityEnforcer.isAuthorized(ModelAuthorizationAction.RUN_REPORT.getUrl(), null, params, null, task, result);
+ }
+}
diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConfiguredConnectorInstanceEntry.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConfiguredConnectorInstanceEntry.java
index ab7b3d429b0..fe27d7aa9e7 100644
--- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConfiguredConnectorInstanceEntry.java
+++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConfiguredConnectorInstanceEntry.java
@@ -48,4 +48,11 @@ public void setConnectorInstance(ConnectorInstance connectorInstance) {
this.connectorInstance = connectorInstance;
}
+ @Override
+ public String toString() {
+ return "ConfiguredConnectorInstanceEntry{" +
+ "connectorOid='" + connectorOid + '\'' +
+ ", connectorInstance=" + connectorInstance +
+ '}';
+ }
}
diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConnectorManager.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConnectorManager.java
index 36ab2500462..938641ce982 100644
--- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConnectorManager.java
+++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConnectorManager.java
@@ -97,6 +97,7 @@ public void unregister() {
}
private static final Trace LOGGER = TraceManager.getTrace(ConnectorManager.class);
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(ConnectorManager.class.getName() + ".content");
private static final String CONNECTOR_INSTANCE_CACHE_NAME = ConnectorManager.class.getName() + ".connectorInstanceCache";
private static final String CONNECTOR_TYPE_CACHE_NAME = ConnectorManager.class.getName() + ".connectorTypeCache";
@@ -691,4 +692,12 @@ public Collection getStateInformation() {
.size(connectorBeanCache.size())
);
}
+
+ @Override
+ public void dumpContent() {
+ if (LOGGER_CONTENT.isInfoEnabled()) {
+ connectorInstanceCache.forEach((k, v) -> LOGGER_CONTENT.info("Cached connector instance: {}: {}", k, v));
+ connectorBeanCache.forEach((k, v) -> LOGGER_CONTENT.info("Cached connector bean: {}: {}", k, v));
+ }
+ }
}
diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConstraintsChecker.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConstraintsChecker.java
index 388e784e247..ff4abd54817 100644
--- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConstraintsChecker.java
+++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ConstraintsChecker.java
@@ -43,7 +43,7 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstraintsCheckingStrategyType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
-import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
+
import org.jetbrains.annotations.NotNull;
/**
@@ -383,6 +383,8 @@ public String toString() {
public static class Cache extends AbstractThreadLocalCache {
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(Cache.class.getName() + ".content");
+
private final Set conflictFreeSituations = ConcurrentHashMap.newKeySet();
private static boolean isOk(String resourceOid, String knownShadowOid, QName objectClassName, QName attributeName,
@@ -455,14 +457,6 @@ private static Cache getCache() {
return cacheInstances.get(Thread.currentThread());
}
- public static void remove(PolyStringType name) {
- Cache cache = getCache();
- if (name != null && cache != null) {
- log("Cache REMOVE for {}", false, name);
- cache.conflictFreeSituations.remove(name.getOrig());
- }
- }
-
@Override
public String description() {
return "conflict-free situations: " + conflictFreeSituations;
@@ -472,6 +466,14 @@ public String description() {
protected int getSize() {
return conflictFreeSituations.size();
}
+
+ @Override
+ protected void dumpContent(String threadName) {
+ if (LOGGER_CONTENT.isInfoEnabled()) {
+ conflictFreeSituations.forEach(situation ->
+ LOGGER_CONTENT.info("Cached conflict-free situation [{}]: {}", threadName, situation));
+ }
+ }
}
private static void log(String message, boolean info, Object... params) {
diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java
index 3b9cb3ec1da..956b4d7c7f8 100644
--- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java
+++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java
@@ -48,6 +48,7 @@
public class ResourceCache implements Cacheable {
private static final Trace LOGGER = TraceManager.getTrace(ResourceCache.class);
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(ResourceCache.class.getName() + ".content");
@Autowired private PrismContext prismContext;
@Autowired private CacheRegistry cacheRegistry;
@@ -65,8 +66,8 @@ public void unregister() {
}
/**
- * Note that prism objects in this map are always immutable. And they must remain immutable after getting them
- * from the cache.
+ * Note that prism objects in this map are always not null and immutable.
+ * And they must remain immutable after getting them from the cache.
*
* As for ConcurrentHashMap: Although we use synchronization whenever possible, let's be extra cautious here.
*/
@@ -199,4 +200,12 @@ public synchronized Collection getStateInformat
.size(cache.size())
);
}
+
+ @Override
+ public void dumpContent() {
+ if (LOGGER_CONTENT.isInfoEnabled()) {
+ cache.forEach((oid, resource) -> LOGGER_CONTENT.info("Cached resource: {}: {} (version: {})",
+ oid, resource, resource.getVersion()));
+ }
+ }
}
diff --git a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/Cacheable.java b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/Cacheable.java
index 5ba577e05ab..79c2edddc39 100644
--- a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/Cacheable.java
+++ b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/Cacheable.java
@@ -18,4 +18,6 @@ public interface Cacheable {
@NotNull
Collection getStateInformation();
+
+ void dumpContent();
}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/AbstractGlobalCacheValue.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/AbstractGlobalCacheValue.java
new file mode 100644
index 00000000000..8107d4b2652
--- /dev/null
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/AbstractGlobalCacheValue.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2020 Evolveum and contributors
+ *
+ * This work is dual-licensed under the Apache License 2.0
+ * and European Union Public License. See LICENSE file for details.
+ */
+
+package com.evolveum.midpoint.repo.cache;
+
+class AbstractGlobalCacheValue {
+
+ /**
+ * When the value was crated. Used only for diagnostic purposes.
+ * (Cache eviction is managed by cache2k itself!)
+ */
+ private final long createdAt = System.currentTimeMillis();
+
+ long getAge() {
+ return System.currentTimeMillis() - createdAt;
+ }
+}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheDispatcherImpl.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheDispatcherImpl.java
index 3b161aef63a..cb659c14ef6 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheDispatcherImpl.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheDispatcherImpl.java
@@ -12,7 +12,6 @@
import com.evolveum.midpoint.repo.api.CacheListener;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
-import com.evolveum.midpoint.xml.ns._public.common.api_types_3.UserSessionManagementType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import java.util.ArrayList;
@@ -26,7 +25,7 @@ public class CacheDispatcherImpl implements CacheDispatcher {
private static final Trace LOGGER = TraceManager.getTrace(CacheDispatcherImpl.class);
- private List cacheListeners = new ArrayList<>();
+ private final List cacheListeners = new ArrayList<>();
@Override
public synchronized void registerCacheListener(CacheListener cacheListener) {
@@ -53,5 +52,4 @@ public void dispatchInvalidation(Class type, String oi
listener.invalidate(type, oid, clusterwide, context);
}
}
-
}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheRegistry.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheRegistry.java
index 727346f8b90..591f779ba30 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheRegistry.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/CacheRegistry.java
@@ -25,7 +25,7 @@
@Component
public class CacheRegistry implements CacheListener {
- private List cacheableServices = new ArrayList<>();
+ private final List cacheableServices = new ArrayList<>();
@Autowired private CacheDispatcher dispatcher;
@Autowired private PrismContext prismContext;
@@ -69,5 +69,9 @@ public CachesStateInformationType getStateInformation() {
cacheableServices.forEach(cacheable -> rv.getEntry().addAll(cacheable.getStateInformation()));
return rv;
}
+
+ public void dumpContent() {
+ cacheableServices.forEach(Cacheable::dumpContent);
+ }
}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectValue.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectValue.java
index 9a0a6744f25..d8d40023ebf 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectValue.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectValue.java
@@ -10,48 +10,49 @@
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
+import org.jetbrains.annotations.NotNull;
+
/**
* Created by Viliam Repan (lazyman).
*/
-public class GlobalCacheObjectValue {
+class GlobalCacheObjectValue extends AbstractGlobalCacheValue {
- private PrismObject object;
+ @NotNull private final PrismObject object;
- private long timeToLive;
+ private long timeToCheckVersion;
- public GlobalCacheObjectValue(PrismObject object, long timeToLive) {
+ GlobalCacheObjectValue(@NotNull PrismObject object, long timeToCheckVersion) {
this.object = object;
- this.timeToLive = timeToLive;
+ this.timeToCheckVersion = timeToCheckVersion;
}
- public long getTimeToLive() {
- return timeToLive;
+ long getTimeToCheckVersion() {
+ return timeToCheckVersion;
}
- public String getObjectOid() {
+ String getObjectOid() {
return object.getOid();
}
- public Class> getObjectType() {
+ Class> getObjectType() {
return object.getCompileTimeClass();
}
- public String getObjectVersion() {
+ String getObjectVersion() {
return object.getVersion();
}
- public PrismObject getObject() {
+ @NotNull PrismObject getObject() {
return object; // cloning is done in RepositoryCache
}
- public void setTimeToLive(long timeToLive) {
- this.timeToLive = timeToLive;
+ void setTimeToCheckVersion(long timeToCheckVersion) {
+ this.timeToCheckVersion = timeToCheckVersion;
}
@Override
public String toString() {
- return "CacheObject{" + "ttl=" + timeToLive
- + ", object=" + object
- + '}';
+ return "GlobalCacheObjectValue{" + "timeToCheckVersion=" + timeToCheckVersion + " (" + (timeToCheckVersion-System.currentTimeMillis()) + " left)"
+ + ", object=" + object + " (version " + object.getVersion() + ")}";
}
}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectVersionValue.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectVersionValue.java
index 70ceb0f02e8..11dc9491122 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectVersionValue.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheObjectVersionValue.java
@@ -10,10 +10,7 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import org.jetbrains.annotations.NotNull;
-/**
- *
- */
-public class GlobalCacheObjectVersionValue {
+public class GlobalCacheObjectVersionValue extends AbstractGlobalCacheValue {
@NotNull private final Class> objectType;
private final String version;
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheQueryValue.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheQueryValue.java
new file mode 100644
index 00000000000..051d2a71bca
--- /dev/null
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalCacheQueryValue.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010-2019 Evolveum and contributors
+ *
+ * This work is dual-licensed under the Apache License 2.0
+ * and European Union Public License. See LICENSE file for details.
+ */
+
+package com.evolveum.midpoint.repo.cache;
+
+import com.evolveum.midpoint.schema.SearchResultList;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
+
+public class GlobalCacheQueryValue extends AbstractGlobalCacheValue {
+
+ @NotNull private final SearchResultList result;
+
+ GlobalCacheQueryValue(@NotNull SearchResultList result) {
+ this.result = result;
+ }
+
+ public @NotNull SearchResultList getResult() {
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "GlobalCacheQueryValue{" +
+ "result=" + result +
+ '}';
+ }
+}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalObjectCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalObjectCache.java
index ac738f397e3..c785f245596 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalObjectCache.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalObjectCache.java
@@ -31,6 +31,7 @@
public class GlobalObjectCache extends AbstractGlobalCache {
private static final Trace LOGGER = TraceManager.getTrace(GlobalObjectCache.class);
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(GlobalObjectCache.class.getName() + ".content");
private static final String CACHE_NAME = "objectCache";
@@ -140,4 +141,15 @@ Collection getStateInformation() {
return Collections.emptySet();
}
}
+
+ void dumpContent() {
+ if (cache != null && LOGGER_CONTENT.isInfoEnabled()) {
+ cache.invokeAll(cache.keys(), e -> {
+ String key = e.getKey();
+ GlobalCacheObjectValue> value = e.getValue();
+ LOGGER_CONTENT.info("Cached object: {}: {} (cached {} ms ago)", key, value, value.getAge());
+ return null;
+ });
+ }
+ }
}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalQueryCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalQueryCache.java
index fa20f1281fa..3136bfa1784 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalQueryCache.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalQueryCache.java
@@ -14,9 +14,12 @@
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SingleCacheStateInformationType;
+
+import org.apache.commons.lang3.tuple.MutablePair;
import org.cache2k.Cache2kBuilder;
import org.cache2k.expiry.ExpiryPolicy;
import org.cache2k.processor.EntryProcessor;
+import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
@@ -33,10 +36,11 @@
public class GlobalQueryCache extends AbstractGlobalCache {
private static final Trace LOGGER = TraceManager.getTrace(GlobalQueryCache.class);
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(GlobalQueryCache.class.getName() + ".content");
private static final String CACHE_NAME = "queryCache";
- private org.cache2k.Cache cache;
+ private org.cache2k.Cache cache;
public void initialize() {
if (cache != null) {
@@ -48,7 +52,7 @@ public void initialize() {
LOGGER.warn("Capacity for " + getCacheType() + " is set to 0; this cache will be disabled (until system restart)");
cache = null;
} else {
- cache = new Cache2kBuilder() {}
+ cache = new Cache2kBuilder() {}
.name(CACHE_NAME)
.entryCapacity(capacity)
.expiryPolicy(getExpirePolicy())
@@ -57,7 +61,7 @@ public void initialize() {
}
}
- private ExpiryPolicy getExpirePolicy() {
+ private ExpiryPolicy getExpirePolicy() {
return (key, value, loadTime, oldEntry) -> getExpiryTime(key.getType());
}
@@ -74,8 +78,13 @@ public boolean isAvailable() {
}
public SearchResultList> get(QueryKey key) {
- //noinspection unchecked
- return cache != null ? cache.peek(key) : null;
+ if (cache != null) {
+ GlobalCacheQueryValue value = cache.peek(key);
+ //noinspection unchecked
+ return value != null ? value.getResult() : null;
+ } else {
+ return null;
+ }
}
public void remove(QueryKey cacheKey) {
@@ -84,13 +93,14 @@ public void remove(QueryKey cacheKey) {
}
}
- public void put(QueryKey key, SearchResultList> cacheObject) {
+ public void put(QueryKey key, @NotNull SearchResultList> cacheObject) {
if (cache != null) {
- cache.put(key, cacheObject);
+ //noinspection unchecked
+ cache.put(key, new GlobalCacheQueryValue(cacheObject));
}
}
- public void invokeAll(EntryProcessor entryProcessor) {
+ void invokeAll(EntryProcessor entryProcessor) {
if (cache != null) {
cache.invokeAll(cache.keys(), entryProcessor);
}
@@ -113,25 +123,50 @@ public void clear() {
}
Collection getStateInformation() {
- Map, Integer> counts = new HashMap<>();
- AtomicInteger size = new AtomicInteger(0);
+ Map, MutablePair> counts = new HashMap<>();
+ AtomicInteger queries = new AtomicInteger(0);
+ AtomicInteger objects = new AtomicInteger(0);
if (cache != null) {
cache.invokeAll(cache.keys(), e -> {
- Class> objectType = e.getKey().getType();
- counts.compute(objectType, (type, count) -> count != null ? count+1 : 1);
- size.incrementAndGet();
+ QueryKey queryKey = e.getKey();
+ Class> objectType = queryKey.getType();
+ int resultingObjects = e.getValue().getResult().size();
+ MutablePair value = counts.get(objectType);
+ if (value == null) {
+ value = new MutablePair<>(0, 0);
+ counts.put(objectType, value);
+ }
+ value.setLeft(value.getLeft() + 1);
+ value.setRight(value.getRight() + resultingObjects);
+ queries.incrementAndGet();
+ objects.addAndGet(resultingObjects);
return null;
});
SingleCacheStateInformationType info = new SingleCacheStateInformationType(prismContext)
.name(GlobalQueryCache.class.getName())
- .size(size.get());
- counts.forEach((type, count) ->
+ .size(queries.get())
+ .secondarySize(objects.get());
+ counts.forEach((type, pair) ->
info.beginComponent()
.name(type.getSimpleName())
- .size(count));
+ .size(pair.getLeft())
+ .secondarySize(pair.getRight()));
return Collections.singleton(info);
} else {
return Collections.emptySet();
}
}
+
+ void dumpContent() {
+ if (cache != null && LOGGER_CONTENT.isInfoEnabled()) {
+ cache.invokeAll(cache.keys(), e -> {
+ QueryKey key = e.getKey();
+ GlobalCacheQueryValue> value = e.getValue();
+ @NotNull SearchResultList> queryResult = value.getResult();
+ LOGGER_CONTENT.info("Cached query of {} ({} object(s), cached {} ms ago): {}: {}",
+ key.getType().getSimpleName(), queryResult.size(), value.getAge(), key.getQuery(), queryResult);
+ return null;
+ });
+ }
+ }
}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalVersionCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalVersionCache.java
index a44ab4a027a..7ee451c4b61 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalVersionCache.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/GlobalVersionCache.java
@@ -32,6 +32,7 @@
public class GlobalVersionCache extends AbstractGlobalCache {
private static final Trace LOGGER = TraceManager.getTrace(GlobalVersionCache.class);
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(GlobalVersionCache.class.getName() + ".content");
private static final String CACHE_NAME = "versionCache";
@@ -149,4 +150,13 @@ public void put(String oid, Class extends ObjectType> type, String version) {
cache.put(oid, new GlobalCacheObjectVersionValue<>(type, version));
}
}
+
+ void dumpContent() {
+ if (cache != null && LOGGER_CONTENT.isInfoEnabled()) {
+ cache.invokeAll(cache.keys(), e -> {
+ LOGGER_CONTENT.info("Cached version: {}: {} (cached {} ms ago)", e.getKey(), e.getValue(), e.getValue().getAge());
+ return null;
+ });
+ }
+ }
}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalObjectCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalObjectCache.java
index d62f0418194..ea959110a17 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalObjectCache.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalObjectCache.java
@@ -9,6 +9,8 @@
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.util.caching.AbstractThreadLocalCache;
+import com.evolveum.midpoint.util.logging.Trace;
+import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import java.util.Map;
@@ -19,6 +21,8 @@
*/
public class LocalObjectCache extends AbstractThreadLocalCache {
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(LocalObjectCache.class.getName() + ".content");
+
private final Map> data = new ConcurrentHashMap<>();
public PrismObject extends ObjectType> get(String oid) {
@@ -42,4 +46,10 @@ public String description() {
protected int getSize() {
return data.size();
}
+
+ public void dumpContent(String threadName) {
+ if (LOGGER_CONTENT.isInfoEnabled()) {
+ data.forEach((k, v) -> LOGGER_CONTENT.info("Cached object [{}] {}: {}", threadName, k, v));
+ }
+ }
}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalQueryCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalQueryCache.java
index 68debd8eac5..cba25e2edbe 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalQueryCache.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalQueryCache.java
@@ -9,6 +9,10 @@
import com.evolveum.midpoint.schema.SearchResultList;
import com.evolveum.midpoint.util.caching.AbstractThreadLocalCache;
+import com.evolveum.midpoint.util.logging.Trace;
+import com.evolveum.midpoint.util.logging.TraceManager;
+
+import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.Map;
@@ -19,13 +23,15 @@
*/
public class LocalQueryCache extends AbstractThreadLocalCache {
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(LocalQueryCache.class.getName() + ".content");
+
private final Map data = new ConcurrentHashMap<>();
public SearchResultList get(QueryKey key) {
return data.get(key);
}
- public void put(QueryKey key, SearchResultList objects) {
+ public void put(QueryKey key, @NotNull SearchResultList objects) {
data.put(key, objects);
}
@@ -43,7 +49,31 @@ protected int getSize() {
return data.size();
}
- public Iterator> getEntryIterator() {
+ public void dumpContent(String threadName) {
+ if (LOGGER_CONTENT.isInfoEnabled()) {
+ data.forEach((k, v) -> LOGGER_CONTENT.info("Cached query [{}] of {} ({} object(s)): {}: {}", threadName,
+ k.getType(), v.size(), k.getQuery(), v));
+ }
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ static int getTotalCachedObjects(ConcurrentHashMap cacheInstances) {
+ int rv = 0;
+ for (LocalQueryCache cacheInstance : cacheInstances.values()) {
+ rv += cacheInstance.getCachedObjects();
+ }
+ return rv;
+ }
+
+ private int getCachedObjects() {
+ int rv = 0;
+ for (SearchResultList value : data.values()) {
+ rv += value.size();
+ }
+ return rv;
+ }
+
+ Iterator> getEntryIterator() {
return data.entrySet().iterator();
}
}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalVersionCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalVersionCache.java
index f5c8c6b323e..25a87ac5f13 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalVersionCache.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/LocalVersionCache.java
@@ -8,6 +8,8 @@
package com.evolveum.midpoint.repo.cache;
import com.evolveum.midpoint.util.caching.AbstractThreadLocalCache;
+import com.evolveum.midpoint.util.logging.Trace;
+import com.evolveum.midpoint.util.logging.TraceManager;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -17,6 +19,8 @@
*/
public class LocalVersionCache extends AbstractThreadLocalCache {
+ private static final Trace LOGGER_CONTENT = TraceManager.getTrace(LocalVersionCache.class.getName() + ".content");
+
private final Map data = new ConcurrentHashMap<>();
public String get(String oid) {
@@ -40,4 +44,10 @@ public String description() {
protected int getSize() {
return data.size();
}
+
+ public void dumpContent(String threadName) {
+ if (LOGGER_CONTENT.isInfoEnabled()) {
+ data.forEach((k, v) -> LOGGER_CONTENT.info("Cached version [{}] {}: {}", threadName, k, v));
+ }
+ }
}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/QueryKey.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/QueryKey.java
index f4f4a00f526..807e230dd08 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/QueryKey.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/QueryKey.java
@@ -10,6 +10,8 @@
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
+import org.jetbrains.annotations.NotNull;
+
import java.util.Objects;
/**
@@ -18,11 +20,11 @@
*/
public class QueryKey {
- private final Class extends ObjectType> type;
+ @NotNull private final Class extends ObjectType> type;
private final ObjectQuery query;
private Integer cachedHashCode;
- QueryKey(Class type, ObjectQuery query) {
+ QueryKey(@NotNull Class type, ObjectQuery query) {
this.type = type;
this.query = query != null ? query.clone() : null;
}
@@ -48,7 +50,7 @@ public int hashCode() {
return cachedHashCode;
}
- public Class extends ObjectType> getType() {
+ @NotNull public Class extends ObjectType> getType() {
return type;
}
@@ -59,7 +61,7 @@ public ObjectQuery getQuery() {
@Override
public String toString() {
return "QueryKey{" +
- "type=" + type +
+ "type=" + type.getSimpleName() +
", query=" + query +
'}';
}
diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java
index d47f5b524dd..d283ff1a244 100644
--- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java
+++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/RepositoryCache.java
@@ -329,7 +329,7 @@ public PrismObject getObject(Class type, String oid
}
objectToReturn = loadAndCacheObject(type, oid, options, readOnly, localObjectsCache, local.supports, result);
} else {
- cacheObject.setTimeToLive(System.currentTimeMillis() + getTimeToVersionCheck(global.typeConfig,
+ cacheObject.setTimeToCheckVersion(System.currentTimeMillis() + getTimeToVersionCheck(global.typeConfig,
global.cacheConfig)); // version matches, renew ttl
collector.registerWeakHit(GlobalObjectCache.class, type, global.statisticsLevel);
log("Cache (global): HIT with version check - getObject {} ({})", global.traceMiss, oid,
@@ -1212,7 +1212,7 @@ private void clearQueryResultsGlobally(Class type, Str
globalQueryCache.invokeAll(entry -> {
QueryKey queryKey = entry.getKey();
all.incrementAndGet();
- if (change.mayAffect(queryKey, entry.getValue(), matchingRuleRegistry)) {
+ if (change.mayAffect(queryKey, entry.getValue().getResult(), matchingRuleRegistry)) {
LOGGER.trace("Removing (from global cache) query for type={}, change={}: {}", type, change, queryKey.getQuery());
entry.remove();
removed.incrementAndGet();
@@ -1721,7 +1721,7 @@ private boolean hasVersionChanged(Class extends ObjectType> objectType, String
}
private boolean shouldCheckVersion(GlobalCacheObjectValue object) {
- return object.getTimeToLive() < System.currentTimeMillis();
+ return object.getTimeToCheckVersion() < System.currentTimeMillis();
}
private PrismObject loadAndCacheObject(Class objectClass, String oid,
@@ -1917,15 +1917,26 @@ public Collection getStateInformation() {
rv.add(new SingleCacheStateInformationType(prismContext)
.name(LocalObjectCache.class.getName())
.size(LocalObjectCache.getTotalSize(LOCAL_OBJECT_CACHE_INSTANCE)));
- rv.add(new SingleCacheStateInformationType(prismContext)
- .name(LocalQueryCache.class.getName())
- .size(LocalQueryCache.getTotalSize(LOCAL_QUERY_CACHE_INSTANCE)));
rv.add(new SingleCacheStateInformationType(prismContext)
.name(LocalVersionCache.class.getName())
.size(LocalVersionCache.getTotalSize(LOCAL_VERSION_CACHE_INSTANCE)));
+ rv.add(new SingleCacheStateInformationType(prismContext)
+ .name(LocalQueryCache.class.getName())
+ .size(LocalQueryCache.getTotalSize(LOCAL_QUERY_CACHE_INSTANCE))
+ .secondarySize(LocalQueryCache.getTotalCachedObjects(LOCAL_QUERY_CACHE_INSTANCE)));
rv.addAll(globalObjectCache.getStateInformation());
rv.addAll(globalVersionCache.getStateInformation());
rv.addAll(globalQueryCache.getStateInformation());
return rv;
}
+
+ @Override
+ public void dumpContent() {
+ LocalObjectCache.dumpContent(LOCAL_OBJECT_CACHE_INSTANCE);
+ LocalVersionCache.dumpContent(LOCAL_VERSION_CACHE_INSTANCE);
+ LocalQueryCache.dumpContent(LOCAL_QUERY_CACHE_INSTANCE);
+ globalObjectCache.dumpContent();
+ globalVersionCache.dumpContent();
+ globalQueryCache.dumpContent();
+ }
}
diff --git a/repo/repo-cache/src/test/java/com/evolveum/midpoint/repo/cache/CacheInvalidationPerformanceTest.java b/repo/repo-cache/src/test/java/com/evolveum/midpoint/repo/cache/CacheInvalidationPerformanceTest.java
new file mode 100644
index 00000000000..984299a213e
--- /dev/null
+++ b/repo/repo-cache/src/test/java/com/evolveum/midpoint/repo/cache/CacheInvalidationPerformanceTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2010-2019 Evolveum and contributors
+ *
+ * This work is dual-licensed under the Apache License 2.0
+ * and European Union Public License. See LICENSE file for details.
+ */
+package com.evolveum.midpoint.repo.cache;
+
+import static com.evolveum.midpoint.prism.util.PrismTestUtil.displayCollection;
+
+import static com.evolveum.midpoint.prism.util.PrismTestUtil.getPrismContext;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import javax.annotation.PostConstruct;
+
+import com.evolveum.midpoint.prism.delta.ItemDelta;
+import com.evolveum.midpoint.prism.polystring.PolyString;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Test;
+import org.xml.sax.SAXException;
+
+import com.evolveum.midpoint.prism.query.ObjectQuery;
+import com.evolveum.midpoint.prism.util.PrismTestUtil;
+import com.evolveum.midpoint.schema.*;
+import com.evolveum.midpoint.schema.constants.MidPointConstants;
+import com.evolveum.midpoint.schema.result.OperationResult;
+import com.evolveum.midpoint.test.util.AbstractSpringTest;
+import com.evolveum.midpoint.test.util.InfraTestMixin;
+import com.evolveum.midpoint.util.PrettyPrinter;
+import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException;
+import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
+import com.evolveum.midpoint.util.exception.SchemaException;
+
+/**
+ * Currently not a part of automated test suite.
+ */
+@ContextConfiguration(locations = { "classpath:ctx-repo-cache-test.xml" })
+public class CacheInvalidationPerformanceTest extends AbstractSpringTest implements InfraTestMixin {
+
+ private static final String CLASS_DOT = CacheInvalidationPerformanceTest.class.getName() + ".";
+
+ @Autowired RepositoryCache repositoryCache;
+
+ @BeforeSuite
+ public void setup() throws SchemaException, SAXException, IOException {
+ PrettyPrinter.setDefaultNamespacePrefix(MidPointConstants.NS_MIDPOINT_PUBLIC_PREFIX);
+ PrismTestUtil.resetPrismContext(MidPointPrismContextFactory.FACTORY);
+ }
+
+ @PostConstruct
+ public void initialize() throws SchemaException, ObjectAlreadyExistsException {
+ OperationResult initResult = new OperationResult(CLASS_DOT + "setup");
+ repositoryCache.postInit(initResult);
+ }
+
+ @Test
+ public void test100InvalidationPerformance() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException {
+
+ final int CACHED_SEARCHES = 10000;
+
+ given();
+ OperationResult result = createOperationResult();
+
+ // create the archetype - we should create reasonably sized object, as
+ ArchetypeType archetype = new ArchetypeType(getPrismContext())
+ .name("name-initial")
+ .displayName("some display name")
+ .locality("some locality")
+ .costCenter("some cost center")
+ .beginActivation()
+ .administrativeStatus(ActivationStatusType.ENABLED)
+ .end();
+ repositoryCache.addObject(archetype.asPrismObject(), null, result);
+
+ modifyArchetypeName(archetype, "name-intermediate", "Initial modification duration", result);
+ modifyArchetypeName(archetype, "name", "Initial modification duration (repeated)", result);
+
+ // fill-in cache with queries
+ for (int i = 0; i < CACHED_SEARCHES; i++) {
+ ObjectQuery query = getPrismContext().queryFor(ArchetypeType.class)
+ .item(ArchetypeType.F_NAME).eq(PolyString.fromOrig("name-" + i)).matchingOrig()
+ .or().item(ArchetypeType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS).eq(ActivationStatusType.ARCHIVED)
+ .or().item(ArchetypeType.F_COST_CENTER).eq("cc100").matchingCaseIgnore()
+ .build();
+ repositoryCache.searchObjects(ArchetypeType.class, query, null, result);
+ }
+
+ repositoryCache.dumpContent();
+ Collection stateInformation = repositoryCache.getStateInformation();
+ displayCollection("cache state information", stateInformation);
+
+ when();
+ modifyArchetypeName(archetype, "name-0", "Second modification duration (with cached searches)", result);
+
+ then();
+ }
+
+ private void modifyArchetypeName(ArchetypeType archetype, String name, String label, OperationResult result)
+ throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException {
+ List> itemDeltas = getPrismContext().deltaFor(ArchetypeType.class)
+ .item(ArchetypeType.F_NAME)
+ .replace(PolyString.fromOrig(name))
+ .asItemDeltas();
+
+ long start = System.currentTimeMillis();
+ repositoryCache.modifyObject(ArchetypeType.class, archetype.getOid(), itemDeltas, result);
+ long duration = System.currentTimeMillis() - start;
+ displayValue(label, duration);
+ }
+}
diff --git a/repo/repo-cache/src/test/resources/logback-test.xml b/repo/repo-cache/src/test/resources/logback-test.xml
index cde591929fc..2163ef393d1 100644
--- a/repo/repo-cache/src/test/resources/logback-test.xml
+++ b/repo/repo-cache/src/test/resources/logback-test.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java
index b1e4896040a..b3327950b81 100644
--- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java
+++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/SystemConfigurationCacheableAdapter.java
@@ -66,4 +66,9 @@ public void invalidate(Class> type, String oid, CacheInvalidationContext conte
public Collection getStateInformation() {
return Collections.emptySet();
}
+
+ @Override
+ public void dumpContent() {
+ // nothing to do here
+ }
}
diff --git a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionFactory.java b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionFactory.java
index 3cfbacc5066..a5fc736084f 100644
--- a/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionFactory.java
+++ b/repo/repo-common/src/main/java/com/evolveum/midpoint/repo/common/expression/ExpressionFactory.java
@@ -210,4 +210,9 @@ public Collection getStateInformation() {
.size(cache.size())
);
}
+
+ @Override
+ public void dumpContent() {
+ // Implement eventually
+ }
}
diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractHigherUnitTest.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractHigherUnitTest.java
index 491f25722f0..db46faf68f0 100644
--- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractHigherUnitTest.java
+++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractHigherUnitTest.java
@@ -229,8 +229,8 @@ public QName getName() {
}
@Override
- public boolean isSupported(QName xsdType) {
- return nameMatchingRule.isSupported(xsdType);
+ public boolean supports(QName xsdType) {
+ return nameMatchingRule.supports(xsdType);
}
@Override
diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java
index 4934230d87d..53f1dac46f0 100644
--- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java
+++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java
@@ -964,8 +964,8 @@ public QName getName() {
}
@Override
- public boolean isSupported(QName xsdType) {
- return nameMatchingRule.isSupported(xsdType);
+ public boolean supports(QName xsdType) {
+ return nameMatchingRule.supports(xsdType);
}
@Override