-
-
Notifications
You must be signed in to change notification settings - Fork 180
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds mbean server to track trigger states
- Loading branch information
Showing
4 changed files
with
125 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,12 +26,14 @@ | |
import org.exist.xmldb.XmldbURI; | ||
|
||
import javax.annotation.Nullable; | ||
import java.lang.ref.WeakReference; | ||
import java.util.ArrayDeque; | ||
import java.util.Collections; | ||
import java.util.Deque; | ||
import java.util.Iterator; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.ConcurrentMap; | ||
import java.util.WeakHashMap; | ||
import java.util.function.BiConsumer; | ||
import java.util.function.Consumer; | ||
|
||
|
@@ -42,11 +44,10 @@ | |
* @author <a href="mailto:[email protected]">Adam Retter</a> | ||
*/ | ||
public class TriggerStatePerThread { | ||
|
||
private static final ConcurrentMap<Txn, Deque<TriggerState>> TRIGGER_STATES = new ConcurrentHashMap<>(); | ||
private static final Map<Thread, TriggerStates> TRIGGER_STATES = Collections.synchronizedMap(new WeakHashMap<>()); | ||
|
||
public static void setAndTest(final Txn txn, final Trigger trigger, final TriggerPhase triggerPhase, final TriggerEvent triggerEvent, final XmldbURI src, final @Nullable XmldbURI dst) throws CyclicTriggerException { | ||
final Deque<TriggerState> states = getStates(txn); | ||
final TriggerStates states = getStates(txn); | ||
|
||
if (states.isEmpty()) { | ||
if (triggerPhase != TriggerPhase.BEFORE) { | ||
|
@@ -123,7 +124,7 @@ public static void clearIfFinished(final Txn txn, final TriggerPhase phase) { | |
if (phase == TriggerPhase.AFTER) { | ||
|
||
int depth = 0; | ||
final Deque<TriggerState> states = getStates(txn); | ||
final TriggerStates states = getStates(txn); | ||
for (final Iterator<TriggerState> it = states.descendingIterator(); it.hasNext(); ) { | ||
final TriggerState state = it.next(); | ||
switch (state.triggerPhase) { | ||
|
@@ -144,25 +145,37 @@ public static void clearIfFinished(final Txn txn, final TriggerPhase phase) { | |
} | ||
} | ||
|
||
public static int keys() { | ||
return TRIGGER_STATES.size(); | ||
} | ||
|
||
public static void clearAll() { | ||
TRIGGER_STATES.clear(); | ||
} | ||
|
||
public static void clear(final Txn txn) { | ||
TRIGGER_STATES.remove(txn); | ||
TRIGGER_STATES.remove(Thread.currentThread()); | ||
} | ||
|
||
public static boolean isEmpty(final Txn txn) { | ||
return getStates(txn).isEmpty(); | ||
} | ||
|
||
public static void forEach(BiConsumer<Txn, Deque<TriggerState>> action) { | ||
public static void dumpTriggerStates() { | ||
TRIGGER_STATES.forEach((k, s) -> System.err.format("key: %s, size: %s", k, s.size()).println()); | ||
} | ||
|
||
public static void forEach(BiConsumer<Thread, TriggerStates> action) { | ||
TRIGGER_STATES.forEach(action); | ||
} | ||
|
||
private static Deque<TriggerState> getStates(final Txn txn) { | ||
return TRIGGER_STATES.computeIfAbsent(txn, TriggerStatePerThread::initStates); | ||
private static TriggerStates getStates(final Txn txn) { | ||
Check warning on line 172 in exist-core/src/main/java/org/exist/collections/triggers/TriggerStatePerThread.java Codacy Production / Codacy Static Code Analysisexist-core/src/main/java/org/exist/collections/triggers/TriggerStatePerThread.java#L172
|
||
return TRIGGER_STATES.computeIfAbsent(Thread.currentThread(), key -> new TriggerStates()); | ||
} | ||
|
||
private static Deque<TriggerState> initStates(final Txn txn) { | ||
private static TriggerStates initStates(final Txn txn) { | ||
Check warning on line 176 in exist-core/src/main/java/org/exist/collections/triggers/TriggerStatePerThread.java Codacy Production / Codacy Static Code Analysisexist-core/src/main/java/org/exist/collections/triggers/TriggerStatePerThread.java#L176
|
||
txn.registerListener(new TransactionCleanUp(txn, TriggerStatePerThread::clear)); | ||
return new ArrayDeque<>(); | ||
return new TriggerStates(); | ||
} | ||
|
||
public record TransactionCleanUp(Txn txn, Consumer<Txn> consumer) implements TxnListener { | ||
|
@@ -177,6 +190,36 @@ public void abort() { | |
} | ||
} | ||
|
||
public static final class TriggerStates extends WeakReference<Deque<TriggerState>> { | ||
public TriggerStates() { | ||
super(new ArrayDeque<>()); | ||
} | ||
|
||
public Iterator<TriggerState> descendingIterator() { | ||
return get().descendingIterator(); | ||
} | ||
|
||
public boolean isEmpty() { | ||
return get().isEmpty(); | ||
} | ||
|
||
public int size() { | ||
return get().size(); | ||
} | ||
|
||
public Iterator<TriggerState> iterator() { | ||
return get().iterator(); | ||
} | ||
|
||
public TriggerState peekFirst() { | ||
return get().peekFirst(); | ||
} | ||
|
||
public void addFirst(TriggerState newState) { | ||
get().addFirst(newState); | ||
} | ||
} | ||
|
||
public record TriggerState(Trigger trigger, TriggerPhase triggerPhase, TriggerEvent triggerEvent, XmldbURI src, | ||
@Nullable XmldbURI dst, boolean possiblyCyclic) { | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
exist-core/src/main/java/org/exist/collections/triggers/XQueryTriggerMBean.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.exist.collections.triggers; | ||
|
||
public interface XQueryTriggerMBean { | ||
int getKeys(); | ||
|
||
void clear(); | ||
|
||
String dumpTriggerStates(); | ||
|
||
String listKeys(); | ||
} |
54 changes: 54 additions & 0 deletions
54
exist-core/src/main/java/org/exist/collections/triggers/XQueryTriggerMBeanImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package org.exist.collections.triggers; | ||
|
||
import javax.management.InstanceAlreadyExistsException; | ||
import javax.management.MBeanRegistrationException; | ||
import javax.management.MBeanServer; | ||
import javax.management.MalformedObjectNameException; | ||
import javax.management.NotCompliantMBeanException; | ||
import javax.management.ObjectName; | ||
import javax.management.StandardMBean; | ||
import java.lang.management.ManagementFactory; | ||
import java.util.StringJoiner; | ||
|
||
final class XQueryTriggerMBeanImpl extends StandardMBean implements XQueryTriggerMBean { | ||
|
||
private XQueryTriggerMBeanImpl() throws NotCompliantMBeanException { | ||
super(XQueryTriggerMBean.class); | ||
} | ||
|
||
static void init() { | ||
try { | ||
final ObjectName name = ObjectName.getInstance("org.exist.management.exist", "type", "TriggerStates"); | ||
final MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer(); | ||
if (!platformMBeanServer.isRegistered(name)) { | ||
platformMBeanServer.registerMBean(new XQueryTriggerMBeanImpl(), name); | ||
} | ||
} catch (final MalformedObjectNameException | InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException ex) { | ||
ex.printStackTrace(); | ||
} | ||
} | ||
|
||
@Override | ||
public int getKeys() { | ||
return TriggerStatePerThread.keys(); | ||
} | ||
|
||
@Override | ||
public void clear() { | ||
TriggerStatePerThread.clearAll(); | ||
} | ||
|
||
@Override | ||
public String dumpTriggerStates() { | ||
StringJoiner joiner = new StringJoiner("\n"); | ||
TriggerStatePerThread.forEach((k, v) -> joiner.add("%s: %s".formatted(k, v.size()))); | ||
return joiner.toString(); | ||
} | ||
|
||
@Override | ||
public String listKeys() { | ||
StringJoiner joiner = new StringJoiner("\n"); | ||
TriggerStatePerThread.forEach((k, v) -> joiner.add("%s".formatted(k))); | ||
return joiner.toString(); | ||
} | ||
} |