Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.microsoft.azure.documentdb.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.microsoft.azure.documentdb.DocumentClientException;
/**
* Used internally to provides helper functions to work with session tokens in the Azure Cosmos DB database service.
*/
public class SessionTokenHelper {
private final static Logger logger = LoggerFactory.getLogger(SessionTokenHelper.class);
public static void setPartitionLocalSessionToken(DocumentServiceRequest request, SessionContainer sessionContainer) throws DocumentClientException {
String originalSessionToken = request.getHeaders().get(HttpConstants.HttpHeaders.SESSION_TOKEN);
String partitionKeyRangeId = request.getResolvedPartitionKeyRange().getId();
// Add support for partitioned collections
if (StringUtils.isNotEmpty(originalSessionToken)) {
VectorSessionToken sessionToken = getLocalSessionToken(request, originalSessionToken, partitionKeyRangeId);
if (sessionToken != null) {
request.setSessionToken(sessionToken);
}
} else {
VectorSessionToken sessionToken = sessionContainer.resolvePartitionLocalSessionToken(request, partitionKeyRangeId);
if (sessionToken != null) {
request.setSessionToken(sessionToken);
}
}
if (request.getSessionToken() == null) {
request.getHeaders().remove(HttpConstants.HttpHeaders.SESSION_TOKEN);
logger.trace("Removed partition local session token, partitionKeyRangeId: {}", partitionKeyRangeId);
} else {
String localSessionToken = String.format(
"%s:%s",
partitionKeyRangeId != null? partitionKeyRangeId: "0",
request.getSessionToken().convertToString());
request.getHeaders().put(HttpConstants.HttpHeaders.SESSION_TOKEN, localSessionToken);
logger.trace("Set partition local session token partitionKeyRangeId: {}, sessionToken: {}",
partitionKeyRangeId, localSessionToken);
}
}
private static VectorSessionToken getLocalSessionToken(
DocumentServiceRequest request,
String sessionToken,
String partitionKeyRangeId) throws DocumentClientException {
if (partitionKeyRangeId == null || partitionKeyRangeId.isEmpty()) {
// AddressCache/address resolution didn't produce partition key range id.
// In this case it is a bug.
throw new IllegalStateException("Partition key range Id is absent in the context.");
}
String[] partitionKeyRangesToken = StringUtils.split(sessionToken, ',');
Set partitionKeyRangeSet = new HashSet<>();
partitionKeyRangeSet.add(partitionKeyRangeId);
VectorSessionToken highestSessionToken = null;
if (request.getResolvedPartitionKeyRange() != null && request.getResolvedPartitionKeyRange().getParents() != null) {
partitionKeyRangeSet.addAll(request.getResolvedPartitionKeyRange().getParents());
}
for (String partitionKeyRangeToken : partitionKeyRangesToken) {
String[] items = StringUtils.split(partitionKeyRangeToken, ':');
if (items.length != 2) {
throw new DocumentClientException(HttpStatus.SC_BAD_REQUEST, "Invalid session token value.");
}
if (partitionKeyRangeSet.contains(items[0])) {
VectorSessionToken parsedSessionToken = VectorSessionToken.create(items[1]);
if (parsedSessionToken == null) {
throw new DocumentClientException(HttpStatus.SC_BAD_REQUEST, "Invalid session token value.");
}
if (highestSessionToken == null) {
highestSessionToken = parsedSessionToken;
} else {
highestSessionToken = highestSessionToken.merge(parsedSessionToken);
}
}
}
return highestSessionToken;
}
static VectorSessionToken resolvePartitionLocalSessionToken(DocumentServiceRequest request,
String partitionKeyRangeId,
ConcurrentHashMap rangeIdToTokenMap) {
if (rangeIdToTokenMap != null) {
if (rangeIdToTokenMap.containsKey(partitionKeyRangeId)) {
return rangeIdToTokenMap.get(partitionKeyRangeId);
}
else {
Collection parents = request.getResolvedPartitionKeyRange().getParents();
if (parents != null) {
List parentsList = new ArrayList<>(parents);
for (int i = parentsList.size() - 1; i >= 0; i--) {
String parentId = parentsList.get(i);
if (rangeIdToTokenMap.containsKey(parentId)) {
return rangeIdToTokenMap.get(parentId);
}
}
}
}
}
return null;
}
/**
* Update session token by request and response headers. This also includes removing the session token if
* it is collection deletion operation.
*
* @param sessionContainer the session container
* @param request the request object
* @param responseHeaders a map of response headers
* @throws DocumentClientException
*/
public static void captureSessionToken(SessionContainer sessionContainer,
DocumentServiceRequest request,
Map responseHeaders) throws DocumentClientException {
if (request.getResourceType() != ResourceType.DocumentCollection ||
request.getOperationType() != OperationType.Delete) {
sessionContainer.setSessionToken(request, responseHeaders);
} else {
sessionContainer.clearToken(request);
}
}
/**
* Handle the session token in case of DocumentClientException when message is processed from a StoreModel.
* This method removes the session token when the collection name may be re-used, updates the collection session
* token in Pre-condition or Conflict failures, as well as NotFound exceptions that are not ReadSessionNotAvailable.
* There is no changes to the session cache in other cases.
*
* @param sessionContainer the session container
* @param request the request object
* @param dce the DocumentClientException instance
* @throws DocumentClientException
*/
public static void updateSession(SessionContainer sessionContainer,
DocumentServiceRequest request,
DocumentClientException dce) throws DocumentClientException {
if (request.getIsNameBased() &&
HttpConstants.StatusCodes.NOTFOUND == dce.getStatusCode() &&
dce.getSubStatusCode() != null &&
HttpConstants.SubStatusCodes.READ_SESSION_NOT_AVAILABLE == dce.getSubStatusCode() &&
request.shouldClearSessionTokenOnSessionReadFailure()) {
// There are a few scenarios leading to this exception, such as:
// 1. when the collection was deleted and recreated with the same name
// 2. when a global account with read fail-over in progress and the requests reached the old endpoint
// 3. when a replica is in copying or catchup state
// When we get here, this is a name-based request, which gets NotFound - ReadSessionNotAvailable exception
// from the backend even after a retry. In #2 the request would be redirected to the write endpoint and
// succeed. We should clear the session token, because this might be #1, the collection name might be reused.
// #3 is handled in the else clause corresponding to this.
logger.debug("Clear the token for request {}", request.getResourceAddress());
sessionContainer.clearToken(request);
} else {
if (!request.getResourceType().isMasterResource() &&
(dce.getStatusCode() == HttpConstants.StatusCodes.PRECONDITION_FAILED ||
dce.getStatusCode() == HttpConstants.StatusCodes.CONFLICT ||
(dce.getStatusCode() == HttpConstants.StatusCodes.NOTFOUND &&
dce.getSubStatusCode() != null &&
dce.getSubStatusCode() != HttpConstants.SubStatusCodes.READ_SESSION_NOT_AVAILABLE))) {
SessionTokenHelper.captureSessionToken(sessionContainer, request, dce.getResponseHeaders());
}
}
}
public static VectorSessionToken parse(String sessionToken) throws DocumentClientException {
String[] sessionTokenSegments = StringUtils.split(sessionToken, ':');
String sessionTokenString = sessionTokenSegments[sessionTokenSegments.length - 1];
VectorSessionToken vectorSessionToken = VectorSessionToken.create(sessionTokenString);
if (vectorSessionToken == null) {
throw new DocumentClientException(HttpConstants.StatusCodes.INTERNAL_SERVER_ERROR, String.format("Could not parse the received session token: %s", sessionTokenString));
}
return vectorSessionToken;
}
}