com.atomikos.icatch.Extent Maven / Gradle / Ivy
/**
* Copyright (C) 2000-2023 Atomikos
*
* LICENSE CONDITIONS
*
* See http://www.atomikos.com/Main/WhichLicenseApplies for details.
*/
package com.atomikos.icatch;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
*
*
* The extent carries the information about the 'size' of a propagation
* after it returns: the directly and indirectly invoked servers, and the orphan
* detection information for those.
*
* This interface is a system interface; it should not be handled by application
* level code (besides shipping it around via toString()).
*
* This class (and its parsing) represent the minimum information required for
* transactions to work across remoting calls. All values are required.
*
* In addition, we are liberal in parsing: any additional, unknown values are ignored -
* so future releases can add extra, optional properties and still work with
* installations of this release.
*/
public class Extent {
/**
* Major version indicator. Only change this for future releases
* that add incompatible changes such as adding/removing required properties
* and/or changing the semantics of existing properties. As long as optional properties
* are added/removed, the version can stay the same.
*/
public static final String VERSION ="2019";
private final Map participants = new Hashtable();
private boolean queried = false;
private final Stack directs = new Stack();
private String parentTransactionId; //null for root
public Extent() {
}
public Extent (String parentTransactionId) {
this.parentTransactionId = parentTransactionId;
}
/**
* Creates a new extent with the supplied extent's participants as indirect participants only.
* Intended for use at the importing / server end, when returning an extent to the client.
*
* The resulting extent must then still be completed with a direct participant representing this JVM for 2-phase commit.
*
* @param extent
*/
public Extent(Extent extent) {
this.parentTransactionId = extent.getParentTransactionId();
addRemoteParticipants(extent.participants);
}
public String getParentTransactionId() {
return parentTransactionId;
}
public void addRemoteParticipants(Map participants)
throws IllegalStateException, SysException {
if (participants == null) {
return;
}
Set parts = participants.keySet();
for (String participant : parts) {
Integer count = this.participants.get(participant);
if (count == null) {
count = 0;
}
Integer cnt = participants.get(participant);
count = count.intValue() + cnt.intValue();
this.participants.put(participant, count);
// NOTE: this will replace the old participant, and if
// it is a proxy then the buffered heuristic msgs will
// also be replaced. This loses info if multiple PARALLEL calls
// went to the same FIRST-ORDER server (i.e., directly invoked).
// Never mind, though: it is considered bad practice
// to execute parallel calls if they might act on the same
// data. This is the case if they go to the same directly
// invoked server.
}
}
/**
* @return Map Mapping URIs of remote participants (directly or indirectly invoked)
* to Integer counts that represent the number of invocations detected by each participant.
*/
public Map getRemoteParticipants()
{
queried = true;
return new HashMap(participants);
}
/**
*
* @return Stack A stack of direct participants. Direct participants
* are those that need to be added to the client TM's two-phase
* commit set.
*
* NOTE: If a participant occurs in the direct participant set,
* it will also be part of the remote set.
*/
@SuppressWarnings("unchecked")
public Stack getParticipants ()
{
queried = true;
return (Stack) directs.clone();
}
/**
* Adds a participant to the extent.
* This method is called at the server side, in order to add the work done
* to the two-phase commit set of the calling (client) side, as well as to
* make sure that orphan information is propagated through the system.
*
* @param participant This instance will
*be added to the indirect as well as to the direct participant set.
*
* @param count The number of invocations detected by the adding client.
* @throws IllegalStateException If no longer allowed.
* @throws SysException
*/
public synchronized void add(Participant participant, int count)
throws SysException, IllegalStateException {
Hashtable table = new Hashtable();
table.put(participant.getURI(), count);
addRemoteParticipants(table);
directs.push(participant);
}
/**
* Merges another extent into this one.
*
*@param extent The extent to add.
*
*@throws IllegalStateException If no longer allowed.
*@throws SysException
*/
public synchronized void add ( Extent extent )
throws IllegalStateException, SysException
{
if (queried) throw new IllegalStateException("Adding extent no longer allowed");
addRemoteParticipants(extent.getRemoteParticipants());
Enumeration enumm = extent.getParticipants().elements();
while (enumm.hasMoreElements()) {
Participant part = enumm.nextElement();
directs.push(part);
}
}
@Override
public String toString() {
StringBuffer ret = new StringBuffer();
ret.append("version=").append(VERSION).append(",");
String delimiter = "";
if (parentTransactionId != null) { //should not be null but we never know
ret.append("parent=").append(parentTransactionId);
delimiter = ",";
}
Set alreadyAdded = new HashSet();
for (Participant p : directs) {
ret.append(delimiter).append("uri=").append(p.getURI()).append(",").
append("responseCount=").append(participants.get(p.getURI())).append(",").
append("direct=").append("true");
delimiter = ",";
alreadyAdded.add(p.getURI());
}
for (String pUri : participants.keySet()) {
if (!alreadyAdded.contains(pUri)) {
ret.append(delimiter).
append("uri=").append(pUri).append(",").
append("responseCount=").append(participants.get(pUri)).append(",").
append("direct=").append("false");
}
}
return ret.toString();
}
}