mirror of
https://github.com/apache/cloudstack
synced 2026-08-08 07:47:32 +00:00
313 lines
11 KiB
Java
313 lines
11 KiB
Java
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
package org.apache.cloudstack.context;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Stack;
|
|
import java.util.UUID;
|
|
|
|
import org.apache.log4j.Logger;
|
|
import org.apache.log4j.NDC;
|
|
|
|
import org.apache.cloudstack.managed.threadlocal.ManagedThreadLocal;
|
|
|
|
import com.cloud.exception.CloudAuthenticationException;
|
|
import com.cloud.user.Account;
|
|
import com.cloud.user.User;
|
|
import com.cloud.utils.UuidUtils;
|
|
import com.cloud.utils.db.EntityManager;
|
|
import com.cloud.utils.exception.CloudRuntimeException;
|
|
|
|
/**
|
|
* CallContext records information about the environment the call is made. This
|
|
* class must be always be available in all CloudStack code. Every thread
|
|
* entry point must set the context and remove it when the thread finishes.
|
|
*/
|
|
public class CallContext {
|
|
private static final Logger s_logger = Logger.getLogger(CallContext.class);
|
|
private static ManagedThreadLocal<CallContext> s_currentContext = new ManagedThreadLocal<CallContext>();
|
|
private static ManagedThreadLocal<Stack<CallContext>> s_currentContextStack = new ManagedThreadLocal<Stack<CallContext>>() {
|
|
@Override
|
|
protected Stack<CallContext> initialValue() {
|
|
return new Stack<CallContext>();
|
|
}
|
|
};
|
|
|
|
private String contextId;
|
|
private Account account;
|
|
private long accountId;
|
|
private long startEventId = 0;
|
|
private String eventDescription;
|
|
private String eventDetails;
|
|
private String eventType;
|
|
private User user;
|
|
private long userId;
|
|
private final Map<Object, Object> context = new HashMap<Object, Object>();
|
|
|
|
static EntityManager s_entityMgr;
|
|
|
|
public static void init(EntityManager entityMgr) {
|
|
s_entityMgr = entityMgr;
|
|
}
|
|
|
|
protected CallContext() {
|
|
}
|
|
|
|
protected CallContext(long userId, long accountId, String contextId) {
|
|
this.userId = userId;
|
|
this.accountId = accountId;
|
|
this.contextId = contextId;
|
|
}
|
|
|
|
protected CallContext(User user, Account account, String contextId) {
|
|
this.user = user;
|
|
this.userId = user.getId();
|
|
this.account = account;
|
|
this.accountId = account.getId();
|
|
this.contextId = contextId;
|
|
}
|
|
|
|
public void putContextParameter(Object key, Object value) {
|
|
context.put(key, value);
|
|
}
|
|
|
|
public Object getContextParameter(Object key) {
|
|
return context.get(key);
|
|
}
|
|
|
|
public long getCallingUserId() {
|
|
return userId;
|
|
}
|
|
|
|
public User getCallingUser() {
|
|
if (user == null) {
|
|
user = s_entityMgr.findById(User.class, userId);
|
|
}
|
|
return user;
|
|
}
|
|
|
|
public String getContextId() {
|
|
return contextId;
|
|
}
|
|
|
|
public Account getCallingAccount() {
|
|
if (account == null) {
|
|
account = s_entityMgr.findById(Account.class, accountId);
|
|
}
|
|
return account;
|
|
}
|
|
|
|
public static CallContext current() {
|
|
return s_currentContext.get();
|
|
}
|
|
|
|
/**
|
|
* This method should only be called if you can propagate the context id
|
|
* from another CallContext.
|
|
*
|
|
* @param callingUser calling user
|
|
* @param callingAccount calling account
|
|
* @param contextId context id propagated from another call context
|
|
* @return CallContext
|
|
*/
|
|
public static CallContext register(User callingUser, Account callingAccount, String contextId) {
|
|
return register(callingUser, callingAccount, null, null, contextId);
|
|
}
|
|
|
|
protected static CallContext register(User callingUser, Account callingAccount, Long userId, Long accountId, String contextId) {
|
|
/*
|
|
Unit tests will have multiple times of setup/tear-down call to this, remove assertions to all unit test to run
|
|
|
|
assert s_currentContext.get() == null : "There's a context already so what does this new register context mean? " + s_currentContext.get().toString();
|
|
if (s_currentContext.get() != null) { // FIXME: This should be removed soon. I added this check only to surface all the places that have this problem.
|
|
throw new CloudRuntimeException("There's a context already so what does this new register context mean? " + s_currentContext.get().toString());
|
|
}
|
|
*/
|
|
CallContext callingContext = null;
|
|
if (userId == null || accountId == null) {
|
|
callingContext = new CallContext(callingUser, callingAccount, contextId);
|
|
} else {
|
|
callingContext = new CallContext(userId, accountId, contextId);
|
|
}
|
|
s_currentContext.set(callingContext);
|
|
NDC.push("ctx-" + UuidUtils.first(contextId));
|
|
if (s_logger.isTraceEnabled()) {
|
|
s_logger.trace("Registered: " + callingContext);
|
|
}
|
|
|
|
s_currentContextStack.get().push(callingContext);
|
|
|
|
return callingContext;
|
|
}
|
|
|
|
public static CallContext register(User callingUser, Account callingAccount) {
|
|
return register(callingUser, callingAccount, UUID.randomUUID().toString());
|
|
}
|
|
|
|
public static CallContext registerSystemCallContextOnceOnly() {
|
|
try {
|
|
CallContext context = s_currentContext.get();
|
|
if (context == null) {
|
|
return register(null, null, User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, UUID.randomUUID().toString());
|
|
}
|
|
assert context.getCallingUserId() == User.UID_SYSTEM : "You are calling a very specific method that registers a one time system context. This method is meant for background threads that does processing.";
|
|
return context;
|
|
} catch (Exception e) {
|
|
s_logger.error("Failed to register the system call context.", e);
|
|
throw new CloudRuntimeException("Failed to register system call context", e);
|
|
}
|
|
}
|
|
|
|
public static CallContext register(String callingUserUuid, String callingAccountUuid) {
|
|
Account account = s_entityMgr.findByUuid(Account.class, callingAccountUuid);
|
|
if (account == null) {
|
|
throw new CloudAuthenticationException("The account is no longer current.").add(Account.class, callingAccountUuid);
|
|
}
|
|
|
|
User user = s_entityMgr.findByUuid(User.class, callingUserUuid);
|
|
if (user == null) {
|
|
throw new CloudAuthenticationException("The user is no longer current.").add(User.class, callingUserUuid);
|
|
}
|
|
return register(user, account);
|
|
}
|
|
|
|
public static CallContext register(long callingUserId, long callingAccountId) throws CloudAuthenticationException {
|
|
Account account = s_entityMgr.findById(Account.class, callingAccountId);
|
|
if (account == null) {
|
|
throw new CloudAuthenticationException("The account is no longer current.").add(Account.class, Long.toString(callingAccountId));
|
|
}
|
|
User user = s_entityMgr.findById(User.class, callingUserId);
|
|
if (user == null) {
|
|
throw new CloudAuthenticationException("The user is no longer current.").add(User.class, Long.toString(callingUserId));
|
|
}
|
|
return register(user, account);
|
|
}
|
|
|
|
public static CallContext register(long callingUserId, long callingAccountId, String contextId) throws CloudAuthenticationException {
|
|
Account account = s_entityMgr.findById(Account.class, callingAccountId);
|
|
if (account == null) {
|
|
throw new CloudAuthenticationException("The account is no longer current.").add(Account.class, Long.toString(callingAccountId));
|
|
}
|
|
User user = s_entityMgr.findById(User.class, callingUserId);
|
|
if (user == null) {
|
|
throw new CloudAuthenticationException("The user is no longer current.").add(User.class, Long.toString(callingUserId));
|
|
}
|
|
return register(user, account, contextId);
|
|
}
|
|
|
|
public static void unregisterAll() {
|
|
while (unregister() != null) {
|
|
// NOOP
|
|
}
|
|
}
|
|
|
|
public static CallContext unregister() {
|
|
CallContext context = s_currentContext.get();
|
|
if (context == null) {
|
|
return null;
|
|
}
|
|
s_currentContext.remove();
|
|
if (s_logger.isTraceEnabled()) {
|
|
s_logger.trace("Unregistered: " + context);
|
|
}
|
|
String contextId = context.getContextId();
|
|
String sessionIdOnStack = null;
|
|
String sessionIdPushedToNDC = "ctx-" + UuidUtils.first(contextId);
|
|
while ((sessionIdOnStack = NDC.pop()) != null) {
|
|
if (sessionIdPushedToNDC.equals(sessionIdOnStack)) {
|
|
break;
|
|
}
|
|
if (s_logger.isTraceEnabled()) {
|
|
s_logger.trace("Popping from NDC: " + contextId);
|
|
}
|
|
}
|
|
|
|
Stack<CallContext> stack = s_currentContextStack.get();
|
|
stack.pop();
|
|
|
|
if (!stack.isEmpty()) {
|
|
s_currentContext.set(stack.peek());
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
public void setStartEventId(long startEventId) {
|
|
this.startEventId = startEventId;
|
|
}
|
|
|
|
public long getStartEventId() {
|
|
return startEventId;
|
|
}
|
|
|
|
public long getCallingAccountId() {
|
|
return accountId;
|
|
}
|
|
|
|
public String getCallingAccountUuid() {
|
|
return getCallingAccount().getUuid();
|
|
}
|
|
|
|
public String getCallingUserUuid() {
|
|
return getCallingUser().getUuid();
|
|
}
|
|
|
|
public void setEventDetails(String eventDetails) {
|
|
this.eventDetails = eventDetails;
|
|
}
|
|
|
|
public String getEventDetails() {
|
|
return eventDetails;
|
|
}
|
|
|
|
public String getEventType() {
|
|
return eventType;
|
|
}
|
|
|
|
public void setEventType(String eventType) {
|
|
this.eventType = eventType;
|
|
}
|
|
|
|
public String getEventDescription() {
|
|
return eventDescription;
|
|
}
|
|
|
|
public void setEventDescription(String eventDescription) {
|
|
this.eventDescription = eventDescription;
|
|
}
|
|
|
|
public static void setActionEventInfo(String eventType, String description) {
|
|
CallContext context = CallContext.current();
|
|
if (context != null) {
|
|
context.setEventType(eventType);
|
|
context.setEventDescription(description);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return new StringBuilder("CCtxt[acct=").append(getCallingAccountId())
|
|
.append("; user=")
|
|
.append(getCallingUserId())
|
|
.append("; id=")
|
|
.append(contextId)
|
|
.append("]")
|
|
.toString();
|
|
}
|
|
}
|