Skip to content

Commit

Permalink
Adds mbean server to track trigger states
Browse files Browse the repository at this point in the history
  • Loading branch information
reinhapa committed May 14, 2024
1 parent 2511d26 commit 2c5b318
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

exist-core/src/main/java/org/exist/collections/triggers/TriggerStatePerThread.java#L172

Avoid unused method parameters such as 'txn'.
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

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

exist-core/src/main/java/org/exist/collections/triggers/TriggerStatePerThread.java#L176

Avoid unused private methods such as 'initStates(Txn)'.
txn.registerListener(new TransactionCleanUp(txn, TriggerStatePerThread::clear));
return new ArrayDeque<>();
return new TriggerStates();
}

public record TransactionCleanUp(Txn txn, Consumer<Txn> consumer) implements TxnListener {
Expand All @@ -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) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,11 @@ public class XQueryTrigger extends SAXTrigger implements DocumentTrigger, Collec
private String bindingPrefix = null;
private XQuery service;

public final static String PREPARE_EXCEPTION_MESSAGE = "Error during trigger prepare";
public static final String PREPARE_EXCEPTION_MESSAGE = "Error during trigger prepare";

public XQueryTrigger() {
XQueryTriggerMBeanImpl.init();
}

@Override
public void configure(final DBBroker broker, final Txn transaction, final Collection parent, final Map<String, List<?>> parameters) throws TriggerException {
Expand Down
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();
}
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();
}
}

0 comments on commit 2c5b318

Please sign in to comment.