Revert "CLOUDSTACK-6003 fixing plus refactoring dispatcher" as it breaks API dispatching for commands having Map<String,String> as a parameter type

This reverts commit 447430c3df.

Conflicts:
	api/src/org/apache/cloudstack/api/BaseCmd.java
	server/src/com/cloud/api/ApiDispatcher.java
	server/src/com/cloud/network/as/AutoScaleManagerImpl.java
	server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
This commit is contained in:
Alena Prokharchyk 2014-02-06 14:34:43 -08:00
parent 916728634b
commit 782c530685
25 changed files with 1130 additions and 1937 deletions

View File

@ -48,12 +48,10 @@ public class ApiConstants {
public static final String CLUSTER_ID = "clusterid";
public static final String CLUSTER_NAME = "clustername";
public static final String CLUSTER_TYPE = "clustertype";
public static final String COMMAND = "command";
public static final String COMPONENT = "component";
public static final String CPU_NUMBER = "cpunumber";
public static final String CPU_SPEED = "cpuspeed";
public static final String CREATED = "created";
public static final String CTX_START_EVENT_ID = "ctxStartEventId";
public static final String CUSTOMIZED = "customized";
public static final String CUSTOMIZED_IOPS = "customizediops";
public static final String CUSTOM_ID = "customid";
@ -80,7 +78,6 @@ public class ApiConstants {
public static final String IP6_DNS2 = "ip6dns2";
public static final String DOMAIN = "domain";
public static final String DOMAIN_ID = "domainid";
public static final String DOMAIN__ID = "domainId";
public static final String DURATION = "duration";
public static final String EMAIL = "email";
public static final String END_DATE = "enddate";
@ -211,7 +208,6 @@ public class ApiConstants {
public static final String SENT = "sent";
public static final String SENT_BYTES = "sentbytes";
public static final String SERVICE_OFFERING_ID = "serviceofferingid";
public static final String SESSIONKEY = "sessionkey";
public static final String SHOW_CAPACITIES = "showcapacities";
public static final String SHOW_REMOVED = "showremoved";
public static final String SIZE = "size";
@ -280,7 +276,6 @@ public class ApiConstants {
public static final String NETWORKRATE = "networkrate";
public static final String HOST_TAGS = "hosttags";
public static final String SSH_KEYPAIR = "keypair";
public static final String HTTPMETHOD = "httpmethod";
public static final String HOST_CPU_CAPACITY = "hostcpucapacity";
public static final String HOST_CPU_NUM = "hostcpunum";
public static final String HOST_MEM_CAPACITY = "hostmemcapacity";

View File

@ -17,22 +17,17 @@
package org.apache.cloudstack.api;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.inject.Inject;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.affinity.AffinityGroupService;
import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.network.element.InternalLoadBalancerElementService;
import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService;
import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService;
@ -79,7 +74,6 @@ import com.cloud.user.Account;
import com.cloud.user.AccountService;
import com.cloud.user.DomainService;
import com.cloud.user.ResourceLimitService;
import com.cloud.utils.ReflectUtil;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.db.UUIDManager;
import com.cloud.vm.UserVmService;
@ -103,8 +97,6 @@ public abstract class BaseCmd {
public static Pattern newInputDateFormat = Pattern.compile("[\\d]+-[\\d]+-[\\d]+ [\\d]+:[\\d]+:[\\d]+");
private static final DateFormat s_outputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
protected static final Map<Class<?>, List<Field>> fieldsForCmdClass = new HashMap<Class<?>, List<Field>>();
private Object _responseObject = null;
private Map<String, String> fullUrlParams;
@ -213,7 +205,7 @@ public abstract class BaseCmd {
return httpMethod;
}
public void setHttpMethod(final String method) {
public void setHttpMethod(String method) {
if (method != null) {
if (method.equalsIgnoreCase("GET"))
httpMethod = HTTPMethod.GET;
@ -235,7 +227,7 @@ public abstract class BaseCmd {
return responseType;
}
public void setResponseType(final String responseType) {
public void setResponseType(String responseType) {
this.responseType = responseType;
}
@ -253,7 +245,7 @@ public abstract class BaseCmd {
return _responseObject;
}
public void setResponseObject(final Object responseObject) {
public void setResponseObject(Object responseObject) {
_responseObject = responseObject;
}
@ -261,7 +253,7 @@ public abstract class BaseCmd {
return _mgr;
}
public static String getDateString(final Date date) {
public static String getDateString(Date date) {
if (date == null) {
return "";
}
@ -272,83 +264,101 @@ public abstract class BaseCmd {
return formattedString;
}
protected List<Field> getAllFieldsForClass(final Class<?> clazz) {
List<Field> filteredFields = fieldsForCmdClass.get(clazz);
// If list of fields was not cached yet
if (filteredFields == null) {
final List<Field> allFields = ReflectUtil.getAllFieldsForClass(this.getClass(), BaseCmd.class);
filteredFields = new ArrayList<Field>();
for (final Field field : allFields) {
final Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
if ((parameterAnnotation != null) && parameterAnnotation.expose()) {
filteredFields.add(field);
}
// FIXME: move this to a utils method so that maps can be unpacked and integer/long values can be appropriately cast
@SuppressWarnings({"unchecked", "rawtypes"})
public Map<String, Object> unpackParams(Map<String, String> params) {
Map<String, Object> lowercaseParams = new HashMap<String, Object>();
for (String key : params.keySet()) {
int arrayStartIndex = key.indexOf('[');
int arrayStartLastIndex = key.lastIndexOf('[');
if (arrayStartIndex != arrayStartLastIndex) {
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
}
// Cache the prepared list for future use
fieldsForCmdClass.put(clazz, filteredFields);
}
return filteredFields;
}
if (arrayStartIndex > 0) {
int arrayEndIndex = key.indexOf(']');
int arrayEndLastIndex = key.lastIndexOf(']');
if ((arrayEndIndex < arrayStartIndex) || (arrayEndIndex != arrayEndLastIndex)) {
// malformed parameter
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
}
protected Account getCurrentContextAccount() {
return CallContext.current().getCallingAccount();
}
// Now that we have an array object, check for a field name in the case of a complex object
int fieldIndex = key.indexOf('.');
String fieldName = null;
if (fieldIndex < arrayEndIndex) {
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
} else {
fieldName = key.substring(fieldIndex + 1);
}
/**
* this method doesn't return all the @{link Parameter}, but only the ones exposed
* and allowed for current @{link RoleType}
*
* @return
*/
public List<Field> getParamFields() {
final List<Field> allFields = getAllFieldsForClass(this.getClass());
final List<Field> validFields = new ArrayList<Field>();
final Account caller = getCurrentContextAccount();
// parse the parameter name as the text before the first '[' character
String paramName = key.substring(0, arrayStartIndex);
paramName = paramName.toLowerCase();
for (final Field field : allFields) {
final Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
Map<Integer, Map> mapArray = null;
Map<String, Object> mapValue = null;
String indexStr = key.substring(arrayStartIndex + 1, arrayEndIndex);
int index = 0;
boolean parsedIndex = false;
try {
if (indexStr != null) {
index = Integer.parseInt(indexStr);
parsedIndex = true;
}
} catch (NumberFormatException nfe) {
s_logger.warn("Invalid parameter " + key + " received, unable to parse object array, returning an error.");
}
//TODO: Annotate @Validate on API Cmd classes, FIXME how to process Validate
final RoleType[] allowedRoles = parameterAnnotation.authorized();
boolean roleIsAllowed = true;
if (allowedRoles.length > 0) {
roleIsAllowed = false;
for (final RoleType allowedRole : allowedRoles) {
if (allowedRole.getValue() == caller.getType()) {
roleIsAllowed = true;
break;
if (!parsedIndex) {
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
}
Object value = lowercaseParams.get(paramName);
if (value == null) {
// for now, assume object array with sub fields
mapArray = new HashMap<Integer, Map>();
mapValue = new HashMap<String, Object>();
mapArray.put(Integer.valueOf(index), mapValue);
} else if (value instanceof Map) {
mapArray = (HashMap)value;
mapValue = mapArray.get(Integer.valueOf(index));
if (mapValue == null) {
mapValue = new HashMap<String, Object>();
mapArray.put(Integer.valueOf(index), mapValue);
}
}
}
if (roleIsAllowed) {
validFields.add(field);
// we are ready to store the value for a particular field into the map for this object
mapValue.put(fieldName, params.get(key));
lowercaseParams.put(paramName, mapArray);
} else {
s_logger.debug("Ignoring paremeter " + parameterAnnotation.name() + " as the caller is not authorized to pass it in");
lowercaseParams.put(key.toLowerCase(), params.get(key));
}
}
return validFields;
return lowercaseParams;
}
protected long getInstanceIdFromJobSuccessResult(final String result) {
protected long getInstanceIdFromJobSuccessResult(String result) {
s_logger.debug("getInstanceIdFromJobSuccessResult not overridden in subclass " + this.getClass().getName());
return 0;
}
public static boolean isAdmin(final short accountType) {
public static boolean isAdmin(short accountType) {
return ((accountType == Account.ACCOUNT_TYPE_ADMIN) || (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) ||
(accountType == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_READ_ONLY_ADMIN));
}
public static boolean isRootAdmin(final short accountType) {
public static boolean isRootAdmin(short accountType) {
return ((accountType == Account.ACCOUNT_TYPE_ADMIN));
}
public void setFullUrlParams(final Map<String, String> map) {
public void setFullUrlParams(Map<String, String> map) {
fullUrlParams = map;
}
@ -356,18 +366,18 @@ public abstract class BaseCmd {
return fullUrlParams;
}
public Long finalyzeAccountId(final String accountName, final Long domainId, final Long projectId, final boolean enabledOnly) {
public Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly) {
if (accountName != null) {
if (domainId == null) {
throw new InvalidParameterValueException("Account must be specified with domainId parameter");
}
final Domain domain = _domainService.getDomain(domainId);
Domain domain = _domainService.getDomain(domainId);
if (domain == null) {
throw new InvalidParameterValueException("Unable to find domain by id");
}
final Account account = _accountService.getActiveAccountByName(accountName, domainId);
Account account = _accountService.getActiveAccountByName(accountName, domainId);
if (account != null && account.getType() != Account.ACCOUNT_TYPE_PROJECT) {
if (!enabledOnly || account.getState() == Account.State.enabled) {
return account.getId();
@ -384,12 +394,12 @@ public abstract class BaseCmd {
}
if (projectId != null) {
final Project project = _projectService.getProject(projectId);
Project project = _projectService.getProject(projectId);
if (project != null) {
if (!enabledOnly || project.getState() == Project.State.Active) {
return project.getProjectAccountId();
} else {
final PermissionDeniedException ex =
PermissionDeniedException ex =
new PermissionDeniedException("Can't add resources to the project with specified projectId in state=" + project.getState() +
" as it's no longer active");
ex.addProxyObject(project.getUuid(), "projectId");
@ -401,11 +411,4 @@ public abstract class BaseCmd {
}
return null;
}
/**
* To be overwritten by any class who needs specific validation
*/
public void validateSpecificParameters(final Map<String, Object> params){
// To be overwritten by any class who needs specific validation
}
}

View File

@ -16,10 +16,7 @@
// under the License.
package org.apache.cloudstack.api;
import java.util.Map;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.exception.CSExceptionErrorCode;
public abstract class BaseListCmd extends BaseCmd {
@ -86,7 +83,7 @@ public abstract class BaseListCmd extends BaseCmd {
public Long getPageSizeVal() {
Long defaultPageSize = s_maxPageSize;
final Integer pageSizeInt = getPageSize();
Integer pageSizeInt = getPageSize();
if (pageSizeInt != null) {
defaultPageSize = pageSizeInt.longValue();
}
@ -99,12 +96,12 @@ public abstract class BaseListCmd extends BaseCmd {
public Long getStartIndex() {
Long startIndex = Long.valueOf(0);
final Long pageSizeVal = getPageSizeVal();
Long pageSizeVal = getPageSizeVal();
if (pageSizeVal == null) {
startIndex = null;
} else if (page != null) {
final int pageNum = page.intValue();
int pageNum = page.intValue();
if (pageNum > 0) {
startIndex = Long.valueOf(pageSizeVal * (pageNum - 1));
}
@ -115,25 +112,4 @@ public abstract class BaseListCmd extends BaseCmd {
public ApiCommandJobType getInstanceType() {
return ApiCommandJobType.None;
}
@Override
public void validateSpecificParameters(final Map<String, Object> params){
super.validateSpecificParameters(params);
final Object pageSizeObj = params.get(ApiConstants.PAGE_SIZE);
Long pageSize = null;
if (pageSizeObj != null) {
pageSize = Long.valueOf((String)pageSizeObj);
}
if (params.get(ApiConstants.PAGE) == null &&
pageSize != null &&
!pageSize.equals(BaseListCmd.s_pageSizeUnlimited)) {
final ServerApiException ex = new ServerApiException(ApiErrorCode.PARAM_ERROR, "\"page\" parameter is required when \"pagesize\" is specified");
ex.setCSErrorCode(CSExceptionErrorCode.getCSErrCode(ex.getClass().getName()));
throw ex;
} else if (pageSize == null && (params.get(ApiConstants.PAGE) != null)) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "\"pagesize\" parameter is required when \"page\" is specified");
}
}
}

View File

@ -151,7 +151,7 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd {
}
Account account = null;
if (autoscaleUserId != null) {
final User user = _entityMgr.findById(User.class, autoscaleUserId);
User user = _entityMgr.findById(User.class, autoscaleUserId);
account = _entityMgr.findById(Account.class, user.getAccountId());
} else {
account = CallContext.current().getCallingAccount();
@ -167,21 +167,21 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd {
}
if (otherDeployParams == null)
return;
final String[] keyValues = otherDeployParams.split("&"); // hostid=123, hypervisor=xenserver
for (final String keyValue : keyValues) { // keyValue == "hostid=123"
final String[] keyAndValue = keyValue.split("="); // keyValue = hostid, 123
String[] keyValues = otherDeployParams.split("&"); // hostid=123, hypervisor=xenserver
for (String keyValue : keyValues) { // keyValue == "hostid=123"
String[] keyAndValue = keyValue.split("="); // keyValue = hostid, 123
if (keyAndValue.length != 2) {
throw new InvalidParameterValueException("Invalid parameter in otherDeployParam : " + keyValue);
}
final String paramName = keyAndValue[0]; // hostid
final String paramValue = keyAndValue[1]; // 123
String paramName = keyAndValue[0]; // hostid
String paramValue = keyAndValue[1]; // 123
otherDeployParamMap.put(paramName, paramValue);
}
}
public HashMap<String, Object> getDeployParamMap() {
public HashMap<String, String> getDeployParamMap() {
createOtherDeployParamMap();
final HashMap<String, Object> deployParams = new HashMap<String, Object>(otherDeployParamMap);
HashMap<String, String> deployParams = new HashMap<String, String>(otherDeployParamMap);
deployParams.put("command", "deployVirtualMachine");
deployParams.put("zoneId", zoneId.toString());
deployParams.put("serviceOfferingId", serviceOfferingId.toString());
@ -189,7 +189,7 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd {
return deployParams;
}
public String getOtherDeployParam(final String param) {
public String getOtherDeployParam(String param) {
if (param == null) {
return null;
}
@ -232,19 +232,19 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd {
@Override
public void execute() {
final AutoScaleVmProfile result = _entityMgr.findById(AutoScaleVmProfile.class, getEntityId());
final AutoScaleVmProfileResponse response = _responseGenerator.createAutoScaleVmProfileResponse(result);
AutoScaleVmProfile result = _entityMgr.findById(AutoScaleVmProfile.class, getEntityId());
AutoScaleVmProfileResponse response = _responseGenerator.createAutoScaleVmProfileResponse(result);
response.setResponseName(getCommandName());
setResponseObject(response);
this.setResponseObject(response);
}
@Override
public void create() throws ResourceAllocationException {
final AutoScaleVmProfile result = _autoScaleService.createAutoScaleVmProfile(this);
AutoScaleVmProfile result = _autoScaleService.createAutoScaleVmProfile(this);
if (result != null) {
setEntityId(result.getId());
setEntityUuid(result.getUuid());
this.setEntityId(result.getId());
this.setEntityUuid(result.getUuid());
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Autoscale Vm Profile");
}

View File

@ -50,16 +50,6 @@
<bean id="apiDispatcher" class="com.cloud.api.ApiDispatcher" />
<bean id="dispatchChainFactory" class="com.cloud.api.dispatch.DispatchChainFactory" />
<bean id="paramSemanticValidationWorker" class="com.cloud.api.dispatch.ParamSemanticValidationWorker" />
<bean id="paramProcessWorker" class="com.cloud.api.dispatch.ParamProcessWorker" />
<bean id="paramGenericValidationWorker" class="com.cloud.api.dispatch.ParamGenericValidationWorker" />
<bean id="commandCreationWorker" class="com.cloud.api.dispatch.CommandCreationWorker" />
<bean id="apiResponseHelper" class="com.cloud.api.ApiResponseHelper" />
<bean id="apiServer" class="com.cloud.api.ApiServer">

View File

@ -70,30 +70,30 @@ public class ApiAsyncJobDispatcher extends AdapterBase implements AsyncJobDispat
});
}
protected void runJobInContext(final AsyncJob job) {
protected void runJobInContext(AsyncJob job) {
BaseAsyncCmd cmdObj = null;
try {
final Class<?> cmdClass = Class.forName(job.getCmd());
Class<?> cmdClass = Class.forName(job.getCmd());
cmdObj = (BaseAsyncCmd)cmdClass.newInstance();
cmdObj = ComponentContext.inject(cmdObj);
cmdObj.configure();
cmdObj.setJob(job);
final Type mapType = new TypeToken<Map<String, String>>() {
Type mapType = new TypeToken<Map<String, String>>() {
}.getType();
final Gson gson = ApiGsonHelper.getBuilder().create();
final Map<String, Object> params = gson.fromJson(job.getCmdInfo(), mapType);
Gson gson = ApiGsonHelper.getBuilder().create();
Map<String, String> params = gson.fromJson(job.getCmdInfo(), mapType);
// whenever we deserialize, the UserContext needs to be updated
final String userIdStr = (String) params.get("ctxUserId");
final String acctIdStr = (String) params.get("ctxAccountId");
String userIdStr = params.get("ctxUserId");
String acctIdStr = params.get("ctxAccountId");
Long userId = null;
Account accountObject = null;
if (cmdObj instanceof BaseAsyncCreateCmd) {
final BaseAsyncCreateCmd create = (BaseAsyncCreateCmd)cmdObj;
create.setEntityId(Long.parseLong((String) params.get("id")));
create.setEntityUuid((String) params.get("uuid"));
BaseAsyncCreateCmd create = (BaseAsyncCreateCmd)cmdObj;
create.setEntityId(Long.parseLong(params.get("id")));
create.setEntityUuid(params.get("uuid"));
}
User user = null;
@ -116,19 +116,19 @@ public class ApiAsyncJobDispatcher extends AdapterBase implements AsyncJobDispat
} finally {
CallContext.unregister();
}
} catch (final Throwable e) {
} catch (Throwable e) {
String errorMsg = null;
int errorCode = ApiErrorCode.INTERNAL_ERROR.getHttpCode();
if (!(e instanceof ServerApiException)) {
s_logger.error("Unexpected exception while executing " + job.getCmd(), e);
errorMsg = e.getMessage();
} else {
final ServerApiException sApiEx = (ServerApiException)e;
ServerApiException sApiEx = (ServerApiException)e;
errorMsg = sApiEx.getDescription();
errorCode = sApiEx.getErrorCode().getHttpCode();
}
final ExceptionResponse response = new ExceptionResponse();
ExceptionResponse response = new ExceptionResponse();
response.setErrorCode(errorCode);
response.setErrorText(errorMsg);
response.setResponseName((cmdObj == null) ? "unknowncommandresponse" : cmdObj.getCommandName());

View File

@ -16,67 +16,126 @@
// under the License.
package com.cloud.api;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.InfrastructureEntity;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
import org.apache.cloudstack.api.BaseAsyncCustomIdCmd;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.BaseCmd.CommandType;
import org.apache.cloudstack.api.BaseCustomIdCmd;
import org.apache.cloudstack.api.BaseListCmd;
import org.apache.cloudstack.api.EntityReference;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd;
import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd;
import org.apache.cloudstack.api.command.user.event.ArchiveEventsCmd;
import org.apache.cloudstack.api.command.user.event.DeleteEventsCmd;
import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.jobs.AsyncJob;
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
import org.apache.log4j.Logger;
import com.cloud.api.dispatch.DispatchChain;
import com.cloud.api.dispatch.DispatchChainFactory;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.utils.DateUtil;
import com.cloud.utils.ReflectUtil;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.exception.CSExceptionErrorCode;
import com.cloud.utils.exception.CloudRuntimeException;
public class ApiDispatcher {
private static final Logger s_logger = Logger.getLogger(ApiDispatcher.class.getName());
Long _createSnapshotQueueSizeLimit;
@Inject
AsyncJobManager _asyncMgr = null;
@Inject
AccountManager _accountMgr = null;
@Inject
EntityManager _entityMgr = null;
@Inject()
protected DispatchChainFactory dispatchChainFactory = null;
private static ApiDispatcher s_instance;
protected DispatchChain standardDispatchChain = null;
protected DispatchChain asyncCreationDispatchChain = null;
public static ApiDispatcher getInstance() {
return s_instance;
}
public ApiDispatcher() {
}
@PostConstruct
public void setup() {
standardDispatchChain = dispatchChainFactory.getStandardDispatchChain();
asyncCreationDispatchChain = dispatchChainFactory.getAsyncCreationDispatchChain();
void init() {
s_instance = this;
}
public void setCreateSnapshotQueueSizeLimit(final Long snapshotLimit) {
public void setCreateSnapshotQueueSizeLimit(Long snapshotLimit) {
_createSnapshotQueueSizeLimit = snapshotLimit;
}
public void dispatchCreateCmd(final BaseAsyncCreateCmd cmd, final Map<String, Object> params) throws Exception {
asyncCreationDispatchChain.dispatch(cmd, params);
public void dispatchCreateCmd(BaseAsyncCreateCmd cmd, Map<String, String> params) throws Exception {
processParameters(cmd, params);
cmd.create();
}
public void dispatch(final BaseCmd cmd, final Map<String, Object> params, final boolean execute) throws Exception {
// Let the chain of responsibility dispatch gradually
standardDispatchChain.dispatch(cmd, params);
private void doAccessChecks(BaseCmd cmd, Map<Object, AccessType> entitiesToAccess) {
Account caller = CallContext.current().getCallingAccount();
Account owner = _accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
final CallContext ctx = CallContext.current();
if (cmd instanceof BaseAsyncCreateCmd) {
//check that caller can access the owner account.
_accountMgr.checkAccess(caller, null, true, owner);
}
if (!entitiesToAccess.isEmpty()) {
//check that caller can access the owner account.
_accountMgr.checkAccess(caller, null, true, owner);
for (Object entity : entitiesToAccess.keySet()) {
if (entity instanceof ControlledEntity) {
_accountMgr.checkAccess(caller, entitiesToAccess.get(entity), true, (ControlledEntity)entity);
} else if (entity instanceof InfrastructureEntity) {
//FIXME: Move this code in adapter, remove code from Account manager
}
}
}
}
public void dispatch(BaseCmd cmd, Map<String, String> params, boolean execute) throws Exception {
processParameters(cmd, params);
CallContext ctx = CallContext.current();
if (cmd instanceof BaseAsyncCmd) {
final BaseAsyncCmd asyncCmd = (BaseAsyncCmd)cmd;
final String startEventId = (String) params.get(ApiConstants.CTX_START_EVENT_ID);
BaseAsyncCmd asyncCmd = (BaseAsyncCmd)cmd;
String startEventId = params.get("ctxStartEventId");
ctx.setStartEventId(Long.valueOf(startEventId));
// Synchronise job on the object if needed
@ -107,6 +166,392 @@ public class ApiDispatcher {
}
cmd.execute();
}
@SuppressWarnings({"unchecked", "rawtypes"})
public static void processParameters(BaseCmd cmd, Map<String, String> params) {
Map<Object, AccessType> entitiesToAccess = new HashMap<Object, AccessType>();
Map<String, Object> unpackedParams = cmd.unpackParams(params);
if (cmd instanceof BaseListCmd) {
Object pageSizeObj = unpackedParams.get(ApiConstants.PAGE_SIZE);
Long pageSize = null;
if (pageSizeObj != null) {
pageSize = Long.valueOf((String)pageSizeObj);
}
if ((unpackedParams.get(ApiConstants.PAGE) == null) && (pageSize != null && !pageSize.equals(BaseListCmd.s_pageSizeUnlimited))) {
ServerApiException ex = new ServerApiException(ApiErrorCode.PARAM_ERROR, "\"page\" parameter is required when \"pagesize\" is specified");
ex.setCSErrorCode(CSExceptionErrorCode.getCSErrCode(ex.getClass().getName()));
throw ex;
} else if (pageSize == null && (unpackedParams.get(ApiConstants.PAGE) != null)) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "\"pagesize\" parameter is required when \"page\" is specified");
}
}
List<Field> fields = ReflectUtil.getAllFieldsForClass(cmd.getClass(), BaseCmd.class);
for (Field field : fields) {
Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
if ((parameterAnnotation == null) || !parameterAnnotation.expose()) {
continue;
}
//TODO: Annotate @Validate on API Cmd classes, FIXME how to process Validate
RoleType[] allowedRoles = parameterAnnotation.authorized();
if (allowedRoles.length > 0) {
boolean permittedParameter = false;
Account caller = CallContext.current().getCallingAccount();
for (RoleType allowedRole : allowedRoles) {
if (allowedRole.getValue() == caller.getType()) {
permittedParameter = true;
break;
}
}
if (!permittedParameter) {
s_logger.debug("Ignoring paremeter " + parameterAnnotation.name() + " as the caller is not authorized to pass it in");
continue;
}
}
Object paramObj = unpackedParams.get(parameterAnnotation.name());
if (paramObj == null) {
if (parameterAnnotation.required()) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " +
cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to missing parameter " + parameterAnnotation.name());
}
continue;
}
// marshall the parameter into the correct type and set the field value
try {
setFieldValue(field, cmd, paramObj, parameterAnnotation);
} catch (IllegalArgumentException argEx) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Unable to execute API command " + cmd.getCommandName() + " due to invalid value " + paramObj + " for parameter " +
parameterAnnotation.name());
}
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " +
cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to invalid value " + paramObj + " for parameter " +
parameterAnnotation.name());
} catch (ParseException parseEx) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Invalid date parameter " + paramObj + " passed to command " + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8));
}
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to parse date " + paramObj + " for command " +
cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + ", please pass dates in the format mentioned in the api documentation");
} catch (InvalidParameterValueException invEx) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " +
cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to invalid value. " + invEx.getMessage());
} catch (CloudRuntimeException cloudEx) {
s_logger.error("CloudRuntimeException", cloudEx);
// FIXME: Better error message? This only happens if the API command is not executable, which typically
//means
// there was
// and IllegalAccessException setting one of the parameters.
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Internal error executing API command " +
cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8));
}
//check access on the resource this field points to
try {
ACL checkAccess = field.getAnnotation(ACL.class);
CommandType fieldType = parameterAnnotation.type();
if (checkAccess != null) {
// Verify that caller can perform actions in behalf of vm owner
//acumulate all Controlled Entities together.
//parse the array of resource types and in case of map check access on key or value or both as specified in @acl
//implement external dao for classes that need findByName
//for maps, specify access to be checkd on key or value.
// find the controlled entity DBid by uuid
if (parameterAnnotation.entityType() != null) {
Class<?>[] entityList = parameterAnnotation.entityType()[0].getAnnotation(EntityReference.class).value();
for (Class entity : entityList) {
// Check if the parameter type is a single
// Id or list of id's/name's
switch (fieldType) {
case LIST:
CommandType listType = parameterAnnotation.collectionType();
switch (listType) {
case LONG:
case UUID:
List<Long> listParam = (List<Long>)field.get(cmd);
for (Long entityId : listParam) {
Object entityObj = s_instance._entityMgr.findById(entity, entityId);
entitiesToAccess.put(entityObj, checkAccess.accessType());
}
break;
/*
* case STRING: List<String> listParam =
* new ArrayList<String>(); listParam =
* (List)field.get(cmd); for(String
* entityName: listParam){
* ControlledEntity entityObj =
* (ControlledEntity
* )daoClassInstance(entityId);
* entitiesToAccess.add(entityObj); }
* break;
*/
default:
break;
}
break;
case LONG:
case UUID:
Object entityObj = s_instance._entityMgr.findById(entity, (Long)field.get(cmd));
entitiesToAccess.put(entityObj, checkAccess.accessType());
break;
default:
break;
}
if (ControlledEntity.class.isAssignableFrom(entity)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("ControlledEntity name is:" + entity.getName());
}
}
if (InfrastructureEntity.class.isAssignableFrom(entity)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("InfrastructureEntity name is:" + entity.getName());
}
}
}
}
}
} catch (IllegalArgumentException e) {
s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() +
" is not accessible]");
} catch (IllegalAccessException e) {
s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() +
" is not accessible]");
}
}
//check access on the entities.
getInstance().doAccessChecks(cmd, entitiesToAccess);
}
private static Long translateUuidToInternalId(String uuid, Parameter annotation) {
if (uuid.equals("-1")) {
// FIXME: This is to handle a lot of hardcoded special cases where -1 is sent
// APITODO: Find and get rid of all hardcoded params in API Cmds and service layer
return -1L;
}
Long internalId = null;
// If annotation's empty, the cmd existed before 3.x try conversion to long
boolean isPre3x = annotation.since().isEmpty();
// Match against Java's UUID regex to check if input is uuid string
boolean isUuid = uuid.matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
// Enforce that it's uuid for newly added apis from version 3.x
if (!isPre3x && !isUuid)
return null;
// Allow both uuid and internal id for pre3x apis
if (isPre3x && !isUuid) {
try {
internalId = Long.parseLong(uuid);
} catch (NumberFormatException e) {
internalId = null;
}
if (internalId != null)
return internalId;
}
// There may be multiple entities defined on the @EntityReference of a Response.class
// UUID CommandType would expect only one entityType, so use the first entityType
Class<?>[] entities = annotation.entityType()[0].getAnnotation(EntityReference.class).value();
// Go through each entity which is an interface to a VO class and get a VO object
// Try to getId() for the object using reflection, break on first non-null value
for (Class<?> entity : entities) {
// For backward compatibility, we search within removed entities and let service layer deal
// with removed ones, return empty response or error
Object objVO = s_instance._entityMgr.findByUuidIncludingRemoved(entity, uuid);
if (objVO == null) {
continue;
}
// Invoke the getId method, get the internal long ID
// If that fails hide exceptions as the uuid may not exist
try {
internalId = ((InternalIdentity)objVO).getId();
} catch (IllegalArgumentException e) {
} catch (NullPointerException e) {
}
// Return on first non-null Id for the uuid entity
if (internalId != null)
break;
}
if (internalId == null) {
if (s_logger.isDebugEnabled())
s_logger.debug("Object entity uuid = " + uuid + " does not exist in the database.");
throw new InvalidParameterValueException("Invalid parameter " + annotation.name() + " value=" + uuid +
" due to incorrect long value format, or entity does not exist or due to incorrect parameter annotation for the field in api cmd class.");
}
return internalId;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static void setFieldValue(Field field, BaseCmd cmdObj, Object paramObj, Parameter annotation) throws IllegalArgumentException, ParseException {
try {
field.setAccessible(true);
CommandType fieldType = annotation.type();
switch (fieldType) {
case BOOLEAN:
field.set(cmdObj, Boolean.valueOf(paramObj.toString()));
break;
case DATE:
// This piece of code is for maintaining backward compatibility
// and support both the date formats(Bug 9724)
// Do the date messaging for ListEventsCmd only
if (cmdObj instanceof ListEventsCmd || cmdObj instanceof DeleteEventsCmd || cmdObj instanceof ArchiveEventsCmd ||
cmdObj instanceof ArchiveAlertsCmd || cmdObj instanceof DeleteAlertsCmd) {
boolean isObjInNewDateFormat = isObjInNewDateFormat(paramObj.toString());
if (isObjInNewDateFormat) {
DateFormat newFormat = BaseCmd.NEW_INPUT_FORMAT;
synchronized (newFormat) {
field.set(cmdObj, newFormat.parse(paramObj.toString()));
}
} else {
DateFormat format = BaseCmd.INPUT_FORMAT;
synchronized (format) {
Date date = format.parse(paramObj.toString());
if (field.getName().equals("startDate")) {
date = messageDate(date, 0, 0, 0);
} else if (field.getName().equals("endDate")) {
date = messageDate(date, 23, 59, 59);
}
field.set(cmdObj, date);
}
}
} else {
DateFormat format = BaseCmd.INPUT_FORMAT;
synchronized (format) {
format.setLenient(false);
field.set(cmdObj, format.parse(paramObj.toString()));
}
}
break;
case FLOAT:
// Assuming that the parameters have been checked for required before now,
// we ignore blank or null values and defer to the command to set a default
// value for optional parameters ...
if (paramObj != null && isNotBlank(paramObj.toString())) {
field.set(cmdObj, Float.valueOf(paramObj.toString()));
}
break;
case INTEGER:
// Assuming that the parameters have been checked for required before now,
// we ignore blank or null values and defer to the command to set a default
// value for optional parameters ...
if (paramObj != null && isNotBlank(paramObj.toString())) {
field.set(cmdObj, Integer.valueOf(paramObj.toString()));
}
break;
case LIST:
List listParam = new ArrayList();
StringTokenizer st = new StringTokenizer(paramObj.toString(), ",");
while (st.hasMoreTokens()) {
String token = st.nextToken();
CommandType listType = annotation.collectionType();
switch (listType) {
case INTEGER:
listParam.add(Integer.valueOf(token));
break;
case UUID:
if (token.isEmpty())
break;
Long internalId = translateUuidToInternalId(token, annotation);
listParam.add(internalId);
break;
case LONG: {
listParam.add(Long.valueOf(token));
}
break;
case SHORT:
listParam.add(Short.valueOf(token));
case STRING:
listParam.add(token);
break;
}
}
field.set(cmdObj, listParam);
break;
case UUID:
if (paramObj.toString().isEmpty())
break;
Long internalId = translateUuidToInternalId(paramObj.toString(), annotation);
field.set(cmdObj, internalId);
break;
case LONG:
field.set(cmdObj, Long.valueOf(paramObj.toString()));
break;
case SHORT:
field.set(cmdObj, Short.valueOf(paramObj.toString()));
break;
case STRING:
if ((paramObj != null) && paramObj.toString().length() > annotation.length()) {
s_logger.error("Value greater than max allowed length " + annotation.length() + " for param: " + field.getName());
throw new InvalidParameterValueException("Value greater than max allowed length " + annotation.length() + " for param: " + field.getName());
}
field.set(cmdObj, paramObj.toString());
break;
case TZDATE:
field.set(cmdObj, DateUtil.parseTZDateString(paramObj.toString()));
break;
case MAP:
default:
field.set(cmdObj, paramObj);
break;
}
} catch (IllegalAccessException ex) {
s_logger.error("Error initializing command " + cmdObj.getCommandName() + ", field " + field.getName() + " is not accessible.");
throw new CloudRuntimeException("Internal error initializing parameters for command " + cmdObj.getCommandName() + " [field " + field.getName() +
" is not accessible]");
}
}
private static boolean isObjInNewDateFormat(String string) {
Matcher matcher = BaseCmd.newInputDateFormat.matcher(string);
return matcher.matches();
}
private static Date messageDate(Date date, int hourOfDay, int minute, int second) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, second);
return cal.getTime();
}
public static void plugService(Field field, BaseCmd cmd) {
Class<?> fc = field.getType();
Object instance = null;
if (instance == null) {
throw new CloudRuntimeException("Unable to plug service " + fc.getSimpleName() + " in command " + cmd.getClass().getSimpleName());
}
try {
field.setAccessible(true);
field.set(cmd, instance);
} catch (IllegalArgumentException e) {
s_logger.error("IllegalArgumentException at plugService for command " + cmd.getCommandName() + ", field " + field.getName());
throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [Illegal argumet at field " + field.getName() + "]");
} catch (IllegalAccessException e) {
s_logger.error("Error at plugService for command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]");
}
}
}

View File

@ -43,6 +43,8 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
@ -84,7 +86,6 @@ import org.springframework.stereotype.Component;
import org.apache.cloudstack.acl.APIChecker;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
@ -121,7 +122,6 @@ import org.apache.cloudstack.framework.jobs.AsyncJobManager;
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import com.cloud.api.dispatch.DispatchChainFactory;
import com.cloud.api.response.ApiResponseSerializer;
import com.cloud.configuration.Config;
import com.cloud.domain.Domain;
@ -159,13 +159,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
private static final Logger s_logger = Logger.getLogger(ApiServer.class.getName());
private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName());
public static boolean encodeApiResponse = false;
public static String jsonContentType = "text/javascript";
private static boolean encodeApiResponse = false;
private static String jsonContentType = "text/javascript";
private static String controlCharacters = "[\000-\011\013-\014\016-\037\177]"; // Non-printable ASCII characters - numbers 0 to 31 and 127 decimal
@Inject ApiDispatcher _dispatcher;
@Inject
protected ApiDispatcher _dispatcher;
@Inject()
protected DispatchChainFactory dispatchChainFactory = null;
@Inject
private AccountManager _accountMgr;
@Inject
@ -193,27 +191,27 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
@Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
return true;
}
@Override
public boolean start() {
Integer apiPort = null; // api port, null by default
final SearchCriteria<ConfigurationVO> sc = _configDao.createSearchCriteria();
SearchCriteria<ConfigurationVO> sc = _configDao.createSearchCriteria();
sc.addAnd("name", SearchCriteria.Op.EQ, Config.IntegrationAPIPort.key());
final List<ConfigurationVO> values = _configDao.search(sc, null);
List<ConfigurationVO> values = _configDao.search(sc, null);
if ((values != null) && (values.size() > 0)) {
final ConfigurationVO apiPortConfig = values.get(0);
ConfigurationVO apiPortConfig = values.get(0);
if (apiPortConfig.getValue() != null) {
apiPort = Integer.parseInt(apiPortConfig.getValue());
}
}
final Map<String, String> configs = _configDao.getConfiguration();
final String strSnapshotLimit = configs.get(Config.ConcurrentSnapshotsThresholdPerHost.key());
Map<String, String> configs = _configDao.getConfiguration();
String strSnapshotLimit = configs.get(Config.ConcurrentSnapshotsThresholdPerHost.key());
if (strSnapshotLimit != null) {
final Long snapshotLimit = NumbersUtil.parseLong(strSnapshotLimit, 1L);
Long snapshotLimit = NumbersUtil.parseLong(strSnapshotLimit, 1L);
if (snapshotLimit.longValue() <= 0) {
s_logger.debug("Global config parameter " + Config.ConcurrentSnapshotsThresholdPerHost.toString() + " is less or equal 0; defaulting to unlimited");
} else {
@ -221,20 +219,20 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
}
final Set<Class<?>> cmdClasses = new HashSet<Class<?>>();
for (final PluggableService pluggableService : _pluggableServices) {
Set<Class<?>> cmdClasses = new HashSet<Class<?>>();
for (PluggableService pluggableService : _pluggableServices) {
cmdClasses.addAll(pluggableService.getCommands());
if (s_logger.isDebugEnabled()) {
s_logger.debug("Discovered plugin " + pluggableService.getClass().getSimpleName());
}
}
for (final Class<?> cmdClass : cmdClasses) {
final APICommand at = cmdClass.getAnnotation(APICommand.class);
for (Class<?> cmdClass : cmdClasses) {
APICommand at = cmdClass.getAnnotation(APICommand.class);
if (at == null) {
throw new CloudRuntimeException(String.format("%s is claimed as a API command, but it doesn't have @APICommand annotation", cmdClass.getName()));
}
final String apiName = at.name();
String apiName = at.name();
if (s_apiNameCmdClassMap.containsKey(apiName)) {
s_logger.error("API Cmd class " + cmdClass.getName() + " has non-unique apiname" + apiName);
continue;
@ -243,13 +241,13 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
setEncodeApiResponse(Boolean.valueOf(_configDao.getValue(Config.EncodeApiResponse.key())));
final String jsonType = _configDao.getValue(Config.JavaScriptDefaultContentType.key());
String jsonType = _configDao.getValue(Config.JavaScriptDefaultContentType.key());
if (jsonType != null) {
jsonContentType = jsonType;
}
if (apiPort != null) {
final ListenerThread listenerThread = new ListenerThread(this, apiPort);
ListenerThread listenerThread = new ListenerThread(this, apiPort);
listenerThread.start();
}
@ -260,13 +258,13 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
// If integration api port is not configured, actual OTW requests will be received by ApiServlet
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException, IOException {
public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
// Create StringBuffer to log information in access log
final StringBuffer sb = new StringBuffer();
final HttpServerConnection connObj = (HttpServerConnection)context.getAttribute("http.connection");
StringBuffer sb = new StringBuffer();
HttpServerConnection connObj = (HttpServerConnection)context.getAttribute("http.connection");
if (connObj instanceof SocketHttpServerConnection) {
final InetAddress remoteAddr = ((SocketHttpServerConnection)connObj).getRemoteAddress();
InetAddress remoteAddr = ((SocketHttpServerConnection)connObj).getRemoteAddress();
sb.append(remoteAddr.toString() + " -- ");
}
sb.append(StringUtils.cleanString(request.getRequestLine().toString()));
@ -275,7 +273,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
List<NameValuePair> paramList = null;
try {
paramList = URLEncodedUtils.parse(new URI(request.getRequestLine().getUri()), "UTF-8");
} catch (final URISyntaxException e) {
} catch (URISyntaxException e) {
s_logger.error("Error parsing url request", e);
}
@ -284,9 +282,9 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
// APITODO: Use Guava's (import com.google.common.collect.Multimap;)
// (Immutable)Multimap<String, String> paramMultiMap = HashMultimap.create();
// Map<String, Collection<String>> parameterMap = paramMultiMap.asMap();
final Map parameterMap = new HashMap<String, String[]>();
Map parameterMap = new HashMap<String, String[]>();
String responseType = BaseCmd.RESPONSE_TYPE_XML;
for (final NameValuePair param : paramList) {
for (NameValuePair param : paramList) {
if (param.getName().equalsIgnoreCase("response")) {
responseType = param.getValue();
continue;
@ -306,15 +304,15 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
// always trust commands from API port, user context will always be UID_SYSTEM/ACCOUNT_ID_SYSTEM
CallContext.register(_accountMgr.getSystemUser(), _accountMgr.getSystemAccount());
sb.insert(0, "(userId=" + User.UID_SYSTEM + " accountId=" + Account.ACCOUNT_ID_SYSTEM + " sessionId=" + null + ") ");
final String responseText = handleRequest(parameterMap, responseType, sb);
String responseText = handleRequest(parameterMap, responseType, sb);
sb.append(" 200 " + ((responseText == null) ? 0 : responseText.length()));
writeResponse(response, responseText, HttpStatus.SC_OK, responseType, null);
} catch (final ServerApiException se) {
final String responseText = getSerializedApiError(se, parameterMap, responseType);
} catch (ServerApiException se) {
String responseText = getSerializedApiError(se, parameterMap, responseType);
writeResponse(response, responseText, se.getErrorCode().getHttpCode(), responseType, se.getDescription());
sb.append(" " + se.getErrorCode() + " " + se.getDescription());
} catch (final RuntimeException e) {
} catch (RuntimeException e) {
// log runtime exception like NullPointerException to help identify the source easier
s_logger.error("Unhandled exception, ", e);
throw e;
@ -327,70 +325,77 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
@Override
@SuppressWarnings("rawtypes")
public String handleRequest(final Map rawParams, final String responseType, final StringBuffer auditTrailSb) throws ServerApiException {
final Map<String, Object> params = ParameterHandler.unpackParams(rawParams);
public String handleRequest(Map params, String responseType, StringBuffer auditTrailSb) throws ServerApiException {
String response = null;
String command = null;
String[] command = null;
try {
command = (String) params.get(ApiConstants.COMMAND);
command = (String[])params.get("command");
if (command == null) {
s_logger.error("invalid request, no command sent");
if (s_logger.isTraceEnabled()) {
s_logger.trace("dumping request parameters");
for (final Object key : params.keySet()) {
final String keyStr = (String)key;
final Object value = params.get(key);
s_logger.trace(" key: " + keyStr + ", value: " + ((value == null) ? "'null'" : value));
for (Object key : params.keySet()) {
String keyStr = (String)key;
String[] value = (String[])params.get(key);
s_logger.trace(" key: " + keyStr + ", value: " + ((value == null) ? "'null'" : value[0]));
}
}
throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "Invalid request, no command sent");
} else {
final Map<String, String> paramMap = new HashMap<String, String>();
final Set keys = rawParams.keySet();
final Iterator keysIter = keys.iterator();
Map<String, String> paramMap = new HashMap<String, String>();
Set keys = params.keySet();
Iterator keysIter = keys.iterator();
while (keysIter.hasNext()) {
final String key = (String)keysIter.next();
String key = (String)keysIter.next();
if ("command".equalsIgnoreCase(key)) {
continue;
}
final String[] value = (String[])rawParams.get(key);
String[] value = (String[])params.get(key);
// fail if parameter value contains ASCII control (non-printable) characters
if (value[0] != null) {
Pattern pattern = Pattern.compile(controlCharacters);
Matcher matcher = pattern.matcher(value[0]);
if (matcher.find()) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Received value " + value[0] + " for parameter " + key +
" is invalid, contains illegal ASCII non-printable characters");
}
}
paramMap.put(key, value[0]);
}
final Class<?> cmdClass = getCmdClass(command);
Class<?> cmdClass = getCmdClass(command[0]);
if (cmdClass != null) {
BaseCmd cmdObj = (BaseCmd)cmdClass.newInstance();
cmdObj = ComponentContext.inject(cmdObj);
cmdObj.configure();
cmdObj.setFullUrlParams(paramMap);
cmdObj.setResponseType(responseType);
cmdObj.setHttpMethod(params.get(ApiConstants.HTTPMETHOD).toString());
cmdObj.setHttpMethod(paramMap.get("httpmethod").toString());
// This is where the command is either serialized, or directly dispatched
response = queueCommand(cmdObj, params);
buildAuditTrail(auditTrailSb, command, response);
response = queueCommand(cmdObj, paramMap);
buildAuditTrail(auditTrailSb, command[0], response);
} else {
if (!command.equalsIgnoreCase("login") && !command.equalsIgnoreCase("logout")) {
final String errorString = "Unknown API command: " + command;
if (!command[0].equalsIgnoreCase("login") && !command[0].equalsIgnoreCase("logout")) {
String errorString = "Unknown API command: " + command[0];
s_logger.warn(errorString);
auditTrailSb.append(" " + errorString);
throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, errorString);
}
}
}
} catch (final InvalidParameterValueException ex) {
} catch (InvalidParameterValueException ex) {
s_logger.info(ex.getMessage());
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage(), ex);
} catch (final IllegalArgumentException ex) {
} catch (IllegalArgumentException ex) {
s_logger.info(ex.getMessage());
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage(), ex);
} catch (final PermissionDeniedException ex) {
final ArrayList<ExceptionProxyObject> idList = ex.getIdProxyList();
} catch (PermissionDeniedException ex) {
ArrayList<ExceptionProxyObject> idList = ex.getIdProxyList();
if (idList != null) {
final StringBuffer buf = new StringBuffer();
for (final ExceptionProxyObject obj : idList) {
StringBuffer buf = new StringBuffer();
for (ExceptionProxyObject obj : idList) {
buf.append(obj.getDescription());
buf.append(":");
buf.append(obj.getUuid());
@ -401,10 +406,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
s_logger.info("PermissionDenied: " + ex.getMessage());
}
throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, ex.getMessage(), ex);
} catch (final AccountLimitException ex) {
} catch (AccountLimitException ex) {
s_logger.info(ex.getMessage());
throw new ServerApiException(ApiErrorCode.ACCOUNT_RESOURCE_LIMIT_ERROR, ex.getMessage(), ex);
} catch (final InsufficientCapacityException ex) {
} catch (InsufficientCapacityException ex) {
s_logger.info(ex.getMessage());
String errorMsg = ex.getMessage();
if (CallContext.current().getCallingAccount().getType() != Account.ACCOUNT_TYPE_ADMIN) {
@ -412,10 +417,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
errorMsg = BaseCmd.USER_ERROR_MESSAGE;
}
throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, errorMsg, ex);
} catch (final ResourceAllocationException ex) {
} catch (ResourceAllocationException ex) {
s_logger.info(ex.getMessage());
throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage(), ex);
} catch (final ResourceUnavailableException ex) {
} catch (ResourceUnavailableException ex) {
s_logger.info(ex.getMessage());
String errorMsg = ex.getMessage();
if (CallContext.current().getCallingAccount().getType() != Account.ACCOUNT_TYPE_ADMIN) {
@ -423,11 +428,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
errorMsg = BaseCmd.USER_ERROR_MESSAGE;
}
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, errorMsg, ex);
} catch (final ServerApiException ex) {
} catch (ServerApiException ex) {
s_logger.info(ex.getDescription());
throw ex;
} catch (final Exception ex) {
s_logger.error("unhandled exception executing api command: " + ((command == null) ? "null" : command), ex);
} catch (Exception ex) {
s_logger.error("unhandled exception executing api command: " + ((command == null) ? "null" : command[0]), ex);
String errorMsg = ex.getMessage();
if (CallContext.current().getCallingAccount().getType() != Account.ACCOUNT_TYPE_ADMIN) {
// hide internal details to non-admin user for security reason
@ -439,28 +444,28 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
return response;
}
private String getBaseAsyncResponse(final long jobId, final BaseAsyncCmd cmd) {
final AsyncJobResponse response = new AsyncJobResponse();
private String getBaseAsyncResponse(long jobId, BaseAsyncCmd cmd) {
AsyncJobResponse response = new AsyncJobResponse();
final AsyncJob job = _entityMgr.findById(AsyncJob.class, jobId);
AsyncJob job = _entityMgr.findById(AsyncJob.class, jobId);
response.setJobId(job.getUuid());
response.setResponseName(cmd.getCommandName());
return ApiResponseSerializer.toSerializedString(response, cmd.getResponseType());
}
private String getBaseAsyncCreateResponse(final long jobId, final BaseAsyncCreateCmd cmd, final String objectUuid) {
final CreateCmdResponse response = new CreateCmdResponse();
final AsyncJob job = _entityMgr.findById(AsyncJob.class, jobId);
private String getBaseAsyncCreateResponse(long jobId, BaseAsyncCreateCmd cmd, String objectUuid) {
CreateCmdResponse response = new CreateCmdResponse();
AsyncJob job = _entityMgr.findById(AsyncJob.class, jobId);
response.setJobId(job.getUuid());
response.setId(objectUuid);
response.setResponseName(cmd.getCommandName());
return ApiResponseSerializer.toSerializedString(response, cmd.getResponseType());
}
private String queueCommand(final BaseCmd cmdObj, final Map<String, Object> params) throws Exception {
final CallContext ctx = CallContext.current();
final Long callerUserId = ctx.getCallingUserId();
final Account caller = ctx.getCallingAccount();
private String queueCommand(BaseCmd cmdObj, Map<String, String> params) throws Exception {
CallContext ctx = CallContext.current();
Long callerUserId = ctx.getCallingUserId();
Account caller = ctx.getCallingAccount();
// Queue command based on Cmd super class:
// BaseCmd: cmd is dispatched to ApiDispatcher, executed, serialized and returned.
@ -470,16 +475,16 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
Long objectId = null;
String objectUuid = null;
if (cmdObj instanceof BaseAsyncCreateCmd) {
final BaseAsyncCreateCmd createCmd = (BaseAsyncCreateCmd)cmdObj;
BaseAsyncCreateCmd createCmd = (BaseAsyncCreateCmd)cmdObj;
_dispatcher.dispatchCreateCmd(createCmd, params);
objectId = createCmd.getEntityId();
objectUuid = createCmd.getEntityUuid();
params.put("id", objectId.toString());
} else {
dispatchChainFactory.getStandardDispatchChain().dispatch(cmdObj, params);
ApiDispatcher.processParameters(cmdObj, params);
}
final BaseAsyncCmd asyncCmd = (BaseAsyncCmd)cmdObj;
BaseAsyncCmd asyncCmd = (BaseAsyncCmd)cmdObj;
if (callerUserId != null) {
params.put("ctxUserId", callerUserId.toString());
@ -492,7 +497,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
asyncCmd.setStartEventId(startEventId);
// save the scheduled event
final Long eventId =
Long eventId =
ActionEventUtils.onScheduledActionEvent((callerUserId == null) ? User.UID_SYSTEM : callerUserId, asyncCmd.getEntityOwnerId(), asyncCmd.getEventType(),
asyncCmd.getEventDescription(), startEventId);
if (startEventId == 0) {
@ -503,22 +508,22 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
params.put("ctxStartEventId", String.valueOf(startEventId));
params.put("cmdEventType", asyncCmd.getEventType().toString());
final Long instanceId = (objectId == null) ? asyncCmd.getInstanceId() : objectId;
final AsyncJobVO job =
Long instanceId = (objectId == null) ? asyncCmd.getInstanceId() : objectId;
AsyncJobVO job =
new AsyncJobVO(ctx.getContextId(), callerUserId, caller.getId(), cmdObj.getClass().getName(), ApiGsonHelper.getBuilder().create().toJson(params),
instanceId, asyncCmd.getInstanceType() != null ? asyncCmd.getInstanceType().toString() : null);
job.setDispatcher(_asyncDispatcher.getName());
final long jobId = _asyncMgr.submitAsyncJob(job);
long jobId = _asyncMgr.submitAsyncJob(job);
if (jobId == 0L) {
final String errorMsg = "Unable to schedule async job for command " + job.getCmd();
String errorMsg = "Unable to schedule async job for command " + job.getCmd();
s_logger.warn(errorMsg);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMsg);
}
if (objectId != null) {
final String objUuid = (objectUuid == null) ? objectId.toString() : objectUuid;
String objUuid = (objectUuid == null) ? objectId.toString() : objectUuid;
return getBaseAsyncCreateResponse(jobId, (BaseAsyncCreateCmd)asyncCmd, objUuid);
} else {
SerializationContext.current().setUuidTranslation(true);
@ -545,8 +550,8 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
@SuppressWarnings("unchecked")
private void buildAsyncListResponse(final BaseListCmd command, final Account account) {
final List<ResponseObject> responses = ((ListResponse)command.getResponseObject()).getResponses();
private void buildAsyncListResponse(BaseListCmd command, Account account) {
List<ResponseObject> responses = ((ListResponse)command.getResponseObject()).getResponses();
if (responses != null && responses.size() > 0) {
List<? extends AsyncJob> jobs = null;
@ -561,18 +566,18 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
return;
}
final Map<String, AsyncJob> objectJobMap = new HashMap<String, AsyncJob>();
for (final AsyncJob job : jobs) {
Map<String, AsyncJob> objectJobMap = new HashMap<String, AsyncJob>();
for (AsyncJob job : jobs) {
if (job.getInstanceId() == null) {
continue;
}
final String instanceUuid = ApiDBUtils.findJobInstanceUuid(job);
String instanceUuid = ApiDBUtils.findJobInstanceUuid(job);
objectJobMap.put(instanceUuid, job);
}
for (final ResponseObject response : responses) {
for (ResponseObject response : responses) {
if (response.getObjectId() != null && objectJobMap.containsKey(response.getObjectId())) {
final AsyncJob job = objectJobMap.get(response.getObjectId());
AsyncJob job = objectJobMap.get(response.getObjectId());
response.setJobId(job.getUuid());
response.setJobStatus(job.getStatus().ordinal());
}
@ -580,7 +585,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
}
private void buildAuditTrail(final StringBuffer auditTrailSb, final String command, final String result) {
private void buildAuditTrail(StringBuffer auditTrailSb, String command, String result) {
if (result == null) {
return;
}
@ -593,31 +598,31 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
@Override
public boolean verifyRequest(final Map<String, Object[]> requestParameters, final Long userId) throws ServerApiException {
public boolean verifyRequest(Map<String, Object[]> requestParameters, Long userId) throws ServerApiException {
try {
String apiKey = null;
String secretKey = null;
String signature = null;
String unsignedRequest = null;
final String[] command = (String[])requestParameters.get(ApiConstants.COMMAND);
String[] command = (String[])requestParameters.get("command");
if (command == null) {
s_logger.info("missing command, ignoring request...");
return false;
}
final String commandName = command[0];
String commandName = command[0];
// if userId not null, that mean that user is logged in
if (userId != null) {
final User user = ApiDBUtils.findUserById(userId);
User user = ApiDBUtils.findUserById(userId);
try {
checkCommandAvailable(user, commandName);
} catch (final RequestLimitException ex) {
} catch (RequestLimitException ex) {
s_logger.debug(ex.getMessage());
throw new ServerApiException(ApiErrorCode.API_LIMIT_EXCEED, ex.getMessage());
} catch (final PermissionDeniedException ex) {
} catch (PermissionDeniedException ex) {
s_logger.debug("The given command:" + commandName + " does not exist or it is not available for user with id:" + userId);
throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "The given command does not exist or it is not available for user");
}
@ -632,9 +637,9 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
// - build a request string with sorted params, make sure it's all lowercase
// - sign the request, verify the signature is the same
final List<String> parameterNames = new ArrayList<String>();
List<String> parameterNames = new ArrayList<String>();
for (final Object paramNameObj : requestParameters.keySet()) {
for (Object paramNameObj : requestParameters.keySet()) {
parameterNames.add((String)paramNameObj); // put the name in a list that we'll sort later
}
@ -643,9 +648,9 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
String signatureVersion = null;
String expires = null;
for (final String paramName : parameterNames) {
for (String paramName : parameterNames) {
// parameters come as name/value pairs in the form String/String[]
final String paramValue = ((String[])requestParameters.get(paramName))[0];
String paramValue = ((String[])requestParameters.get(paramName))[0];
if ("signature".equalsIgnoreCase(paramName)) {
signature = paramValue;
@ -683,30 +688,30 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
synchronized (DateFormatToUse) {
try {
expiresTS = DateFormatToUse.parse(expires);
} catch (final ParseException pe) {
} catch (ParseException pe) {
s_logger.debug("Incorrect date format for Expires parameter", pe);
return false;
}
}
final Date now = new Date(System.currentTimeMillis());
Date now = new Date(System.currentTimeMillis());
if (expiresTS.before(now)) {
s_logger.debug("Request expired -- ignoring ...sig: " + signature + ", apiKey: " + apiKey);
return false;
}
}
final TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
txn.close();
User user = null;
// verify there is a user with this api key
final Pair<User, Account> userAcctPair = _accountMgr.findUserByApiKey(apiKey);
Pair<User, Account> userAcctPair = _accountMgr.findUserByApiKey(apiKey);
if (userAcctPair == null) {
s_logger.debug("apiKey does not map to a valid user -- ignoring request, apiKey: " + apiKey);
return false;
}
user = userAcctPair.first();
final Account account = userAcctPair.second();
Account account = userAcctPair.second();
if (user.getState() != Account.State.enabled || !account.getState().equals(Account.State.enabled)) {
s_logger.info("disabled or locked user accessing the api, userid = " + user.getId() + "; name = " + user.getUsername() + "; state: " + user.getState() +
@ -716,10 +721,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
try {
checkCommandAvailable(user, commandName);
} catch (final RequestLimitException ex) {
} catch (RequestLimitException ex) {
s_logger.debug(ex.getMessage());
throw new ServerApiException(ApiErrorCode.API_LIMIT_EXCEED, ex.getMessage());
} catch (final PermissionDeniedException ex) {
} catch (PermissionDeniedException ex) {
s_logger.debug("The given command:" + commandName + " does not exist or it is not available for user");
throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist or it is not available for user with id:"
+ userId);
@ -734,30 +739,30 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
unsignedRequest = unsignedRequest.toLowerCase();
final Mac mac = Mac.getInstance("HmacSHA1");
final SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");
mac.init(keySpec);
mac.update(unsignedRequest.getBytes());
final byte[] encryptedBytes = mac.doFinal();
final String computedSignature = Base64.encodeBase64String(encryptedBytes);
final boolean equalSig = signature.equals(computedSignature);
byte[] encryptedBytes = mac.doFinal();
String computedSignature = Base64.encodeBase64String(encryptedBytes);
boolean equalSig = signature.equals(computedSignature);
if (!equalSig) {
s_logger.info("User signature: " + signature + " is not equaled to computed signature: " + computedSignature);
} else {
CallContext.register(user, account);
}
return equalSig;
} catch (final ServerApiException ex) {
} catch (ServerApiException ex) {
throw ex;
} catch (final Exception ex) {
} catch (Exception ex) {
s_logger.error("unable to verify request signature");
}
return false;
}
@Override
public Long fetchDomainId(final String domainUUID) {
final Domain domain = _domainMgr.getDomain(domainUUID);
public Long fetchDomainId(String domainUUID) {
Domain domain = _domainMgr.getDomain(domainUUID);
if (domain != null)
return domain.getId();
else
@ -765,15 +770,15 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
@Override
public void loginUser(final HttpSession session, final String username, final String password, Long domainId, final String domainPath, final String loginIpAddress,
final Map<String, Object[]> requestParameters) throws CloudAuthenticationException {
public void loginUser(HttpSession session, String username, String password, Long domainId, String domainPath, String loginIpAddress,
Map<String, Object[]> requestParameters) throws CloudAuthenticationException {
// We will always use domainId first. If that does not exist, we will use domain name. If THAT doesn't exist
// we will default to ROOT
if (domainId == null) {
if (domainPath == null || domainPath.trim().length() == 0) {
domainId = Domain.ROOT_DOMAIN;
} else {
final Domain domainObj = _domainMgr.findDomainByPath(domainPath);
Domain domainObj = _domainMgr.findDomainByPath(domainPath);
if (domainObj != null) {
domainId = domainObj.getId();
} else { // if an unknown path is passed in, fail the login call
@ -782,26 +787,26 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
}
final UserAccount userAcct = _accountMgr.authenticateUser(username, password, domainId, loginIpAddress, requestParameters);
UserAccount userAcct = _accountMgr.authenticateUser(username, password, domainId, loginIpAddress, requestParameters);
if (userAcct != null) {
final String timezone = userAcct.getTimezone();
String timezone = userAcct.getTimezone();
float offsetInHrs = 0f;
if (timezone != null) {
final TimeZone t = TimeZone.getTimeZone(timezone);
TimeZone t = TimeZone.getTimeZone(timezone);
s_logger.info("Current user logged in under " + timezone + " timezone");
final java.util.Date date = new java.util.Date();
final long longDate = date.getTime();
final float offsetInMs = (t.getOffset(longDate));
java.util.Date date = new java.util.Date();
long longDate = date.getTime();
float offsetInMs = (t.getOffset(longDate));
offsetInHrs = offsetInMs / (1000 * 60 * 60);
s_logger.info("Timezone offset from UTC is: " + offsetInHrs);
}
final Account account = _accountMgr.getAccount(userAcct.getAccountId());
Account account = _accountMgr.getAccount(userAcct.getAccountId());
// set the userId and account object for everyone
session.setAttribute("userid", userAcct.getId());
final UserVO user = (UserVO)_accountMgr.getActiveUser(userAcct.getId());
UserVO user = (UserVO)_accountMgr.getActiveUser(userAcct.getId());
if (user.getUuid() != null) {
session.setAttribute("user_UUID", user.getUuid());
}
@ -813,7 +818,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
session.setAttribute("account", account.getAccountName());
session.setAttribute("domainid", account.getDomainId());
final DomainVO domain = (DomainVO)_domainMgr.getDomain(account.getDomainId());
DomainVO domain = (DomainVO)_domainMgr.getDomain(account.getDomainId());
if (domain.getUuid() != null) {
session.setAttribute("domain_UUID", domain.getUuid());
}
@ -829,10 +834,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
// (bug 5483) generate a session key that the user must submit on every request to prevent CSRF, add that
// to the login response so that session-based authenticators know to send the key back
final SecureRandom sesssionKeyRandom = new SecureRandom();
final byte sessionKeyBytes[] = new byte[20];
SecureRandom sesssionKeyRandom = new SecureRandom();
byte sessionKeyBytes[] = new byte[20];
sesssionKeyRandom.nextBytes(sessionKeyBytes);
final String sessionKey = Base64.encodeBase64String(sessionKeyBytes);
String sessionKey = Base64.encodeBase64String(sessionKeyBytes);
session.setAttribute("sessionkey", sessionKey);
return;
@ -841,14 +846,14 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
@Override
public void logoutUser(final long userId) {
public void logoutUser(long userId) {
_accountMgr.logoutUser(userId);
return;
}
@Override
public boolean verifyUser(final Long userId) {
final User user = _accountMgr.getUserIncludingRemoved(userId);
public boolean verifyUser(Long userId) {
User user = _accountMgr.getUserIncludingRemoved(userId);
Account account = null;
if (user != null) {
account = _accountMgr.getAccount(user.getAccountId());
@ -862,27 +867,27 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
return true;
}
private void checkCommandAvailable(final User user, final String commandName) throws PermissionDeniedException {
private void checkCommandAvailable(User user, String commandName) throws PermissionDeniedException {
if (user == null) {
throw new PermissionDeniedException("User is null for role based API access check for command" + commandName);
}
for (final APIChecker apiChecker : _apiAccessCheckers) {
for (APIChecker apiChecker : _apiAccessCheckers) {
apiChecker.checkAccess(user, commandName);
}
}
private Class<?> getCmdClass(final String cmdName) {
private Class<?> getCmdClass(String cmdName) {
return s_apiNameCmdClassMap.get(cmdName);
}
// FIXME: rather than isError, we might was to pass in the status code to give more flexibility
private void writeResponse(final HttpResponse resp, final String responseText, final int statusCode, final String responseType, final String reasonPhrase) {
private void writeResponse(HttpResponse resp, final String responseText, final int statusCode, String responseType, String reasonPhrase) {
try {
resp.setStatusCode(statusCode);
resp.setReasonPhrase(reasonPhrase);
final BasicHttpEntity body = new BasicHttpEntity();
BasicHttpEntity body = new BasicHttpEntity();
if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) {
// JSON response
body.setContentType(jsonContentType);
@ -900,7 +905,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
body.setContent(new ByteArrayInputStream(responseText.getBytes("UTF-8")));
}
resp.setEntity(body);
} catch (final Exception ex) {
} catch (Exception ex) {
s_logger.error("error!", ex);
}
}
@ -915,10 +920,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
private ServerSocket _serverSocket = null;
private HttpParams _params = null;
public ListenerThread(final ApiServer requestHandler, final int port) {
public ListenerThread(ApiServer requestHandler, int port) {
try {
_serverSocket = new ServerSocket(port);
} catch (final IOException ioex) {
} catch (IOException ioex) {
s_logger.error("error initializing api server", ioex);
return;
}
@ -931,14 +936,14 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
.setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1");
// Set up the HTTP protocol processor
final BasicHttpProcessor httpproc = new BasicHttpProcessor();
BasicHttpProcessor httpproc = new BasicHttpProcessor();
httpproc.addInterceptor(new ResponseDate());
httpproc.addInterceptor(new ResponseServer());
httpproc.addInterceptor(new ResponseContent());
httpproc.addInterceptor(new ResponseConnControl());
// Set up request handlers
final HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();
HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();
reqistry.register("*", requestHandler);
// Set up the HTTP service
@ -953,15 +958,15 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
while (!Thread.interrupted()) {
try {
// Set up HTTP connection
final Socket socket = _serverSocket.accept();
final DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
Socket socket = _serverSocket.accept();
DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
conn.bind(socket, _params);
// Execute a new worker task to handle the request
s_executor.execute(new WorkerTask(_httpService, conn, s_workerCount++));
} catch (final InterruptedIOException ex) {
} catch (InterruptedIOException ex) {
break;
} catch (final IOException e) {
} catch (IOException e) {
s_logger.error("I/O error initializing connection thread", e);
break;
}
@ -980,33 +985,33 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
@Override
protected void runInContext() {
final HttpContext context = new BasicHttpContext(null);
HttpContext context = new BasicHttpContext(null);
try {
while (!Thread.interrupted() && _conn.isOpen()) {
_httpService.handleRequest(_conn, context);
_conn.close();
}
} catch (final ConnectionClosedException ex) {
} catch (ConnectionClosedException ex) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("ApiServer: Client closed connection");
}
} catch (final IOException ex) {
} catch (IOException ex) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("ApiServer: IOException - " + ex);
}
} catch (final HttpException ex) {
} catch (HttpException ex) {
s_logger.warn("ApiServer: Unrecoverable HTTP protocol violation" + ex);
} finally {
try {
_conn.shutdown();
} catch (final IOException ignore) {
} catch (IOException ignore) {
}
}
}
}
@Override
public String getSerializedApiError(final int errorCode, final String errorText, final Map<String, Object[]> apiCommandParams, final String responseType) {
public String getSerializedApiError(int errorCode, String errorText, Map<String, Object[]> apiCommandParams, String responseType) {
String responseName = null;
Class<?> cmdClass = null;
String responseText = null;
@ -1015,10 +1020,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
if (apiCommandParams == null || apiCommandParams.isEmpty()) {
responseName = "errorresponse";
} else {
final Object cmdObj = apiCommandParams.get(ApiConstants.COMMAND);
Object cmdObj = apiCommandParams.get("command");
// cmd name can be null when "command" parameter is missing in the request
if (cmdObj != null) {
final String cmdName = ((String[])cmdObj)[0];
String cmdName = ((String[])cmdObj)[0];
cmdClass = getCmdClass(cmdName);
if (cmdClass != null) {
responseName = ((BaseCmd)cmdClass.newInstance()).getCommandName();
@ -1027,21 +1032,21 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
}
}
final ExceptionResponse apiResponse = new ExceptionResponse();
ExceptionResponse apiResponse = new ExceptionResponse();
apiResponse.setErrorCode(errorCode);
apiResponse.setErrorText(errorText);
apiResponse.setResponseName(responseName);
SerializationContext.current().setUuidTranslation(true);
responseText = ApiResponseSerializer.toSerializedString(apiResponse, responseType);
} catch (final Exception e) {
} catch (Exception e) {
s_logger.error("Exception responding to http request", e);
}
return responseText;
}
@Override
public String getSerializedApiError(final ServerApiException ex, final Map<String, Object[]> apiCommandParams, final String responseType) {
public String getSerializedApiError(ServerApiException ex, Map<String, Object[]> apiCommandParams, String responseType) {
String responseName = null;
Class<?> cmdClass = null;
String responseText = null;
@ -1054,11 +1059,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
if (ex.getErrorCode() == ApiErrorCode.UNSUPPORTED_ACTION_ERROR || apiCommandParams == null || apiCommandParams.isEmpty()) {
responseName = "errorresponse";
} else {
final Object cmdObj = apiCommandParams.get(ApiConstants.COMMAND);
Object cmdObj = apiCommandParams.get("command");
// cmd name can be null when "command" parameter is missing in
// the request
if (cmdObj != null) {
final String cmdName = ((String[])cmdObj)[0];
String cmdName = ((String[])cmdObj)[0];
cmdClass = getCmdClass(cmdName);
if (cmdClass != null) {
responseName = ((BaseCmd)cmdClass.newInstance()).getCommandName();
@ -1067,11 +1072,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
}
}
final ExceptionResponse apiResponse = new ExceptionResponse();
ExceptionResponse apiResponse = new ExceptionResponse();
apiResponse.setErrorCode(ex.getErrorCode().getHttpCode());
apiResponse.setErrorText(ex.getDescription());
apiResponse.setResponseName(responseName);
final ArrayList<ExceptionProxyObject> idList = ex.getIdProxyList();
ArrayList<ExceptionProxyObject> idList = ex.getIdProxyList();
if (idList != null) {
for (int i = 0; i < idList.size(); i++) {
apiResponse.addProxyObject(idList.get(i));
@ -1084,7 +1089,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
SerializationContext.current().setUuidTranslation(true);
responseText = ApiResponseSerializer.toSerializedString(apiResponse, responseType);
} catch (final Exception e) {
} catch (Exception e) {
s_logger.error("Exception responding to http request", e);
}
return responseText;
@ -1095,7 +1100,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
@Inject
public void setPluggableServices(final List<PluggableService> pluggableServices) {
public void setPluggableServices(List<PluggableService> pluggableServices) {
_pluggableServices = pluggableServices;
}
@ -1104,7 +1109,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
}
@Inject
public void setApiAccessCheckers(final List<APIChecker> apiAccessCheckers) {
public void setApiAccessCheckers(List<APIChecker> apiAccessCheckers) {
_apiAccessCheckers = apiAccessCheckers;
}
@ -1112,7 +1117,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
return encodeApiResponse;
}
private static void setEncodeApiResponse(final boolean encodeApiResponse) {
private static void setEncodeApiResponse(boolean encodeApiResponse) {
ApiServer.encodeApiResponse = encodeApiResponse;
}

View File

@ -35,7 +35,6 @@ import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.ServerApiException;
@ -68,40 +67,40 @@ public class ApiServlet extends HttpServlet {
}
@Override
public void init(final ServletConfig config) throws ServletException {
public void init(ServletConfig config) throws ServletException {
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, config.getServletContext());
}
@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
processRequest(req, resp);
}
@Override
protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
processRequest(req, resp);
}
private void utf8Fixup(final HttpServletRequest req, final Map<String, Object[]> params) {
private void utf8Fixup(HttpServletRequest req, Map<String, Object[]> params) {
if (req.getQueryString() == null) {
return;
}
final String[] paramsInQueryString = req.getQueryString().split("&");
String[] paramsInQueryString = req.getQueryString().split("&");
if (paramsInQueryString != null) {
for (final String param : paramsInQueryString) {
final String[] paramTokens = param.split("=", 2);
for (String param : paramsInQueryString) {
String[] paramTokens = param.split("=", 2);
if (paramTokens != null && paramTokens.length == 2) {
String name = paramTokens[0];
String value = paramTokens[1];
try {
name = URLDecoder.decode(name, "UTF-8");
} catch (final UnsupportedEncodingException e) {
} catch (UnsupportedEncodingException e) {
}
try {
value = URLDecoder.decode(value, "UTF-8");
} catch (final UnsupportedEncodingException e) {
} catch (UnsupportedEncodingException e) {
}
params.put(name, new String[] {value});
} else {
@ -120,13 +119,13 @@ public class ApiServlet extends HttpServlet {
});
}
private void processRequestInContext(final HttpServletRequest req, final HttpServletResponse resp) {
final StringBuffer auditTrailSb = new StringBuffer();
private void processRequestInContext(HttpServletRequest req, HttpServletResponse resp) {
StringBuffer auditTrailSb = new StringBuffer();
auditTrailSb.append(" " + req.getRemoteAddr());
auditTrailSb.append(" -- " + req.getMethod() + " ");
// get the response format since we'll need it in a couple of places
String responseType = BaseCmd.RESPONSE_TYPE_XML;
final Map<String, Object[]> params = new HashMap<String, Object[]>();
Map<String, Object[]> params = new HashMap<String, Object[]>();
params.putAll(req.getParameterMap());
// For HTTP GET requests, it seems that HttpServletRequest.getParameterMap() actually tries
@ -144,19 +143,19 @@ public class ApiServlet extends HttpServlet {
try {
HttpSession session = req.getSession(false);
final Object[] responseTypeParam = params.get(ApiConstants.RESPONSE);
Object[] responseTypeParam = params.get("response");
if (responseTypeParam != null) {
responseType = (String)responseTypeParam[0];
}
final Object[] commandParam = params.get(ApiConstants.COMMAND);
if (commandParam != null) {
final String command = (String)commandParam[0];
Object[] commandObj = params.get("command");
if (commandObj != null) {
String command = (String)commandObj[0];
if ("logout".equalsIgnoreCase(command)) {
// if this is just a logout, invalidate the session and return
if (session != null) {
final Long userId = (Long)session.getAttribute("userid");
final Account account = (Account)session.getAttribute("accountobj");
Long userId = (Long)session.getAttribute("userid");
Account account = (Account)session.getAttribute("accountobj");
Long accountId = null;
if (account != null) {
accountId = account.getId();
@ -167,7 +166,7 @@ public class ApiServlet extends HttpServlet {
}
try {
session.invalidate();
} catch (final IllegalStateException ise) {
} catch (IllegalStateException ise) {
}
}
auditTrailSb.append("command=logout");
@ -180,18 +179,18 @@ public class ApiServlet extends HttpServlet {
if (session != null) {
try {
session.invalidate();
} catch (final IllegalStateException ise) {
} catch (IllegalStateException ise) {
}
}
session = req.getSession(true);
final String[] username = (String[])params.get(ApiConstants.USERNAME);
final String[] password = (String[])params.get(ApiConstants.PASSWORD);
String[] domainIdArr = (String[])params.get(ApiConstants.DOMAIN_ID);
String[] username = (String[])params.get("username");
String[] password = (String[])params.get("password");
String[] domainIdArr = (String[])params.get("domainid");
if (domainIdArr == null) {
domainIdArr = (String[])params.get(ApiConstants.DOMAIN__ID);
domainIdArr = (String[])params.get("domainId");
}
final String[] domainName = (String[])params.get(ApiConstants.DOMAIN);
String[] domainName = (String[])params.get("domain");
Long domainId = null;
if ((domainIdArr != null) && (domainIdArr.length > 0)) {
try {
@ -201,10 +200,10 @@ public class ApiServlet extends HttpServlet {
domainId = new Long(Long.parseLong(domainIdArr[0]));
}
auditTrailSb.append(" domainid=" + domainId);// building the params for POST call
} catch (final NumberFormatException e) {
} catch (NumberFormatException e) {
s_logger.warn("Invalid domain id entered by user");
auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "Invalid domain id entered, please enter a valid one");
final String serializedResponse =
String serializedResponse =
_apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid domain id entered, please enter a valid one", params,
responseType);
writeResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType);
@ -226,24 +225,24 @@ public class ApiServlet extends HttpServlet {
}
if (username != null) {
final String pwd = ((password == null) ? null : password[0]);
String pwd = ((password == null) ? null : password[0]);
try {
_apiServer.loginUser(session, username[0], pwd, domainId, domain, req.getRemoteAddr(), params);
auditTrailSb.insert(0, "(userId=" + session.getAttribute("userid") + " accountId=" + ((Account)session.getAttribute("accountobj")).getId() +
" sessionId=" + session.getId() + ")");
final String loginResponse = getLoginSuccessResponse(session, responseType);
String loginResponse = getLoginSuccessResponse(session, responseType);
writeResponse(resp, loginResponse, HttpServletResponse.SC_OK, responseType);
return;
} catch (final CloudAuthenticationException ex) {
} catch (CloudAuthenticationException ex) {
// TODO: fall through to API key, or just fail here w/ auth error? (HTTP 401)
try {
session.invalidate();
} catch (final IllegalStateException ise) {
} catch (IllegalStateException ise) {
}
auditTrailSb.append(" " + ApiErrorCode.ACCOUNT_ERROR + " " + ex.getMessage() != null ? ex.getMessage()
: "failed to authenticate user, check if username/password are correct");
final String serializedResponse =
String serializedResponse =
_apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), ex.getMessage() != null ? ex.getMessage()
: "failed to authenticate user, check if username/password are correct", params, responseType);
writeResponse(resp, serializedResponse, ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), responseType);
@ -253,7 +252,7 @@ public class ApiServlet extends HttpServlet {
}
}
auditTrailSb.append(req.getQueryString());
final boolean isNew = ((session == null) ? true : session.isNew());
boolean isNew = ((session == null) ? true : session.isNew());
// Initialize an empty context and we will update it after we have verified the request below,
// we no longer rely on web-session here, verifyRequest will populate user/account information
@ -262,17 +261,17 @@ public class ApiServlet extends HttpServlet {
if (!isNew) {
userId = (Long)session.getAttribute("userid");
final String account = (String)session.getAttribute("account");
final Object accountObj = session.getAttribute("accountobj");
final String sessionKey = (String)session.getAttribute("sessionkey");
final String[] sessionKeyParam = (String[])params.get(ApiConstants.SESSIONKEY);
String account = (String)session.getAttribute("account");
Object accountObj = session.getAttribute("accountobj");
String sessionKey = (String)session.getAttribute("sessionkey");
String[] sessionKeyParam = (String[])params.get("sessionkey");
if ((sessionKeyParam == null) || (sessionKey == null) || !sessionKey.equals(sessionKeyParam[0])) {
try {
session.invalidate();
} catch (final IllegalStateException ise) {
} catch (IllegalStateException ise) {
}
auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials");
final String serializedResponse =
String serializedResponse =
_apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials", params, responseType);
writeResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType);
return;
@ -280,25 +279,26 @@ public class ApiServlet extends HttpServlet {
// Do a sanity check here to make sure the user hasn't already been deleted
if ((userId != null) && (account != null) && (accountObj != null) && _apiServer.verifyUser(userId)) {
if (commandParam == null || commandParam[0] == null) {
String[] command = (String[])params.get("command");
if (command == null) {
s_logger.info("missing command, ignoring request...");
auditTrailSb.append(" " + HttpServletResponse.SC_BAD_REQUEST + " " + "no command specified");
final String serializedResponse = _apiServer.getSerializedApiError(HttpServletResponse.SC_BAD_REQUEST, "no command specified", params, responseType);
String serializedResponse = _apiServer.getSerializedApiError(HttpServletResponse.SC_BAD_REQUEST, "no command specified", params, responseType);
writeResponse(resp, serializedResponse, HttpServletResponse.SC_BAD_REQUEST, responseType);
return;
}
final User user = _entityMgr.findById(User.class, userId);
User user = _entityMgr.findById(User.class, userId);
CallContext.register(user, (Account)accountObj);
} else {
// Invalidate the session to ensure we won't allow a request across management server
// restarts if the userId was serialized to the stored session
try {
session.invalidate();
} catch (final IllegalStateException ise) {
} catch (IllegalStateException ise) {
}
auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials");
final String serializedResponse =
String serializedResponse =
_apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials", params, responseType);
writeResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType);
return;
@ -326,29 +326,29 @@ public class ApiServlet extends HttpServlet {
// Add the HTTP method (GET/POST/PUT/DELETE) as well into the params map.
params.put("httpmethod", new String[] {req.getMethod()});
final String response = _apiServer.handleRequest(params, responseType, auditTrailSb);
String response = _apiServer.handleRequest(params, responseType, auditTrailSb);
writeResponse(resp, response != null ? response : "", HttpServletResponse.SC_OK, responseType);
} else {
if (session != null) {
try {
session.invalidate();
} catch (final IllegalStateException ise) {
} catch (IllegalStateException ise) {
}
}
auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials and/or request signature");
final String serializedResponse =
String serializedResponse =
_apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials and/or request signature", params,
responseType);
writeResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType);
}
} catch (final ServerApiException se) {
final String serializedResponseText = _apiServer.getSerializedApiError(se, params, responseType);
} catch (ServerApiException se) {
String serializedResponseText = _apiServer.getSerializedApiError(se, params, responseType);
resp.setHeader("X-Description", se.getDescription());
writeResponse(resp, serializedResponseText, se.getErrorCode().getHttpCode(), responseType);
auditTrailSb.append(" " + se.getErrorCode() + " " + se.getDescription());
} catch (final Exception ex) {
} catch (Exception ex) {
s_logger.error("unknown exception writing api response", ex);
auditTrailSb.append(" unknown exception writing api response");
} finally {
@ -372,7 +372,7 @@ public class ApiServlet extends HttpServlet {
*/
// FIXME: rather than isError, we might was to pass in the status code to give more flexibility
private void writeResponse(final HttpServletResponse resp, final String response, final int responseCode, final String responseType) {
private void writeResponse(HttpServletResponse resp, String response, int responseCode, String responseType) {
try {
if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) {
resp.setContentType(ApiServer.getJsonContentType() + "; charset=UTF-8");
@ -382,11 +382,11 @@ public class ApiServlet extends HttpServlet {
resp.setStatus(responseCode);
resp.getWriter().print(response);
} catch (final IOException ioex) {
} catch (IOException ioex) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("exception writing response: " + ioex);
}
} catch (final Exception ex) {
} catch (Exception ex) {
if (!(ex instanceof IllegalStateException)) {
s_logger.error("unknown exception writing api response", ex);
}
@ -394,29 +394,29 @@ public class ApiServlet extends HttpServlet {
}
@SuppressWarnings("rawtypes")
private String getLoginSuccessResponse(final HttpSession session, final String responseType) {
final StringBuffer sb = new StringBuffer();
final int inactiveInterval = session.getMaxInactiveInterval();
private String getLoginSuccessResponse(HttpSession session, String responseType) {
StringBuffer sb = new StringBuffer();
int inactiveInterval = session.getMaxInactiveInterval();
final String user_UUID = (String)session.getAttribute("user_UUID");
String user_UUID = (String)session.getAttribute("user_UUID");
session.removeAttribute("user_UUID");
final String domain_UUID = (String)session.getAttribute("domain_UUID");
String domain_UUID = (String)session.getAttribute("domain_UUID");
session.removeAttribute("domain_UUID");
if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) {
sb.append("{ \"loginresponse\" : { ");
final Enumeration attrNames = session.getAttributeNames();
Enumeration attrNames = session.getAttributeNames();
if (attrNames != null) {
sb.append("\"timeout\" : \"" + inactiveInterval + "\"");
while (attrNames.hasMoreElements()) {
final String attrName = (String)attrNames.nextElement();
String attrName = (String)attrNames.nextElement();
if ("userid".equalsIgnoreCase(attrName)) {
sb.append(", \"" + attrName + "\" : \"" + user_UUID + "\"");
} else if ("domainid".equalsIgnoreCase(attrName)) {
sb.append(", \"" + attrName + "\" : \"" + domain_UUID + "\"");
} else {
final Object attrObj = session.getAttribute(attrName);
Object attrObj = session.getAttribute(attrName);
if ((attrObj instanceof String) || (attrObj instanceof Long)) {
sb.append(", \"" + attrName + "\" : \"" + attrObj.toString() + "\"");
}
@ -428,16 +428,16 @@ public class ApiServlet extends HttpServlet {
sb.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
sb.append("<loginresponse cloud-stack-version=\"" + ApiDBUtils.getVersion() + "\">");
sb.append("<timeout>" + inactiveInterval + "</timeout>");
final Enumeration attrNames = session.getAttributeNames();
Enumeration attrNames = session.getAttributeNames();
if (attrNames != null) {
while (attrNames.hasMoreElements()) {
final String attrName = (String)attrNames.nextElement();
String attrName = (String)attrNames.nextElement();
if ("userid".equalsIgnoreCase(attrName)) {
sb.append("<" + attrName + ">" + user_UUID + "</" + attrName + ">");
} else if ("domainid".equalsIgnoreCase(attrName)) {
sb.append("<" + attrName + ">" + domain_UUID + "</" + attrName + ">");
} else {
final Object attrObj = session.getAttribute(attrName);
Object attrObj = session.getAttribute(attrName);
if (attrObj instanceof String || attrObj instanceof Long || attrObj instanceof Short) {
sb.append("<" + attrName + ">" + attrObj.toString() + "</" + attrName + ">");
}
@ -450,8 +450,8 @@ public class ApiServlet extends HttpServlet {
return sb.toString();
}
private String getLogoutSuccessResponse(final String responseType) {
final StringBuffer sb = new StringBuffer();
private String getLogoutSuccessResponse(String responseType) {
StringBuffer sb = new StringBuffer();
if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) {
sb.append("{ \"logoutresponse\" : { \"description\" : \"success\" } }");
} else {

View File

@ -1,138 +0,0 @@
// 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 com.cloud.api;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ServerApiException;
public class ParameterHandler {
private static final Logger s_logger = Logger.getLogger(ParameterHandler.class.getName());
/**
* Non-printable ASCII characters - numbers 0 to 31 and 127 decimal
*/
public static final String CONTROL_CHARACTERS = "[\000-\011\013-\014\016-\037\177]";
@SuppressWarnings({"unchecked", "rawtypes"})
public static Map<String, Object> unpackParams(final Map params) {
final Map<String, String> stringMap = new HashMap<String, String>();
final Set keys = params.keySet();
final Iterator keysIter = keys.iterator();
while (keysIter.hasNext()) {
final String key = (String)keysIter.next();
final String[] value = (String[])params.get(key);
// fail if parameter value contains ASCII control (non-printable) characters
if (value[0] != null) {
final Pattern pattern = Pattern.compile(CONTROL_CHARACTERS);
final Matcher matcher = pattern.matcher(value[0]);
if (matcher.find()) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Received value " + value[0] + " for parameter " + key +
" is invalid, contains illegal ASCII non-printable characters");
}
}
stringMap.put(key, value[0]);
}
final Map<String, Object> lowercaseParams = new HashMap<String, Object>();
for (final Object keyObject : stringMap.keySet()) {
final String key = (String) keyObject;
final int arrayStartIndex = key.indexOf('[');
final int arrayStartLastIndex = key.lastIndexOf('[');
if (arrayStartIndex != arrayStartLastIndex) {
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
}
if (arrayStartIndex > 0) {
final int arrayEndIndex = key.indexOf(']');
final int arrayEndLastIndex = key.lastIndexOf(']');
if ((arrayEndIndex < arrayStartIndex) || (arrayEndIndex != arrayEndLastIndex)) {
// malformed parameter
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
}
// Now that we have an array object, check for a field name in the case of a complex object
final int fieldIndex = key.indexOf('.');
String fieldName = null;
if (fieldIndex < arrayEndIndex) {
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
} else {
fieldName = key.substring(fieldIndex + 1);
}
// parse the parameter name as the text before the first '[' character
String paramName = key.substring(0, arrayStartIndex);
paramName = paramName.toLowerCase();
Map<Integer, Map> mapArray = null;
Map<String, Object> mapValue = null;
final String indexStr = key.substring(arrayStartIndex + 1, arrayEndIndex);
int index = 0;
boolean parsedIndex = false;
try {
if (indexStr != null) {
index = Integer.parseInt(indexStr);
parsedIndex = true;
}
} catch (final NumberFormatException nfe) {
s_logger.warn("Invalid parameter " + key + " received, unable to parse object array, returning an error.");
}
if (!parsedIndex) {
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
}
final Object value = lowercaseParams.get(paramName);
if (value == null) {
// for now, assume object array with sub fields
mapArray = new HashMap<Integer, Map>();
mapValue = new HashMap<String, Object>();
mapArray.put(Integer.valueOf(index), mapValue);
} else if (value instanceof Map) {
mapArray = (HashMap)value;
mapValue = mapArray.get(Integer.valueOf(index));
if (mapValue == null) {
mapValue = new HashMap<String, Object>();
mapArray.put(Integer.valueOf(index), mapValue);
}
}
// we are ready to store the value for a particular field into the map for this object
mapValue.put(fieldName, stringMap.get(key));
lowercaseParams.put(paramName, mapArray);
} else {
lowercaseParams.put(key.toLowerCase(), stringMap.get(key));
}
}
return lowercaseParams;
}
}

View File

@ -1,55 +0,0 @@
// 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 com.cloud.api.dispatch;
import java.util.Map;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.ServerApiException;
import com.cloud.exception.ResourceAllocationException;
/**
* This worker validates parameters in a semantic way, that is of
* course specific for each {@link BaseCmd}, so actually it delegates
* the validation on the {@link BaseCmd} itself
*
* @author afornie
*/
public class CommandCreationWorker implements DispatchWorker {
@Override
public boolean handle(final BaseCmd cmd, final Map<String, Object> params) {
if (cmd instanceof BaseAsyncCreateCmd) {
try {
((BaseAsyncCreateCmd)cmd).create();
} catch (final ResourceAllocationException e) {
throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR,
e.getMessage(), e);
}
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
"Trying to invoke creation on a Command that is not BaseAsyncCreateCmd");
}
return true;
}
}

View File

@ -1,45 +0,0 @@
// 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 com.cloud.api.dispatch;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.ServerApiException;
public class DispatchChain {
protected List<DispatchWorker> workers = new ArrayList<DispatchWorker>();
public DispatchChain add(final DispatchWorker worker) {
workers.add(worker);
return this;
}
public void dispatch(final BaseCmd cmd, final Map<String, Object> params)
throws ServerApiException {
for (final DispatchWorker worker : workers) {
if (!worker.handle(cmd, params)) {
break;
}
}
}
}

View File

@ -1,68 +0,0 @@
// 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 com.cloud.api.dispatch;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import com.cloud.user.AccountManager;
public class DispatchChainFactory {
@Inject
protected AccountManager _accountMgr = null;
@Inject
protected ParamGenericValidationWorker paramGenericValidationWorker = null;
@Inject
protected ParamProcessWorker paramProcessWorker = null;
@Inject
protected ParamSemanticValidationWorker paramSemanticValidationWorker = null;
@Inject
protected CommandCreationWorker commandCreationWorker = null;
protected DispatchChain standardDispatchChain = null;
protected DispatchChain asyncCreationDispatchChain = null;
@PostConstruct
public void setup() {
standardDispatchChain = new DispatchChain().
add(paramGenericValidationWorker).
add(paramProcessWorker).
add(paramSemanticValidationWorker);
asyncCreationDispatchChain = new DispatchChain().
add(paramGenericValidationWorker).
add(paramProcessWorker).
add(paramSemanticValidationWorker).
add(commandCreationWorker);
}
public DispatchChain getStandardDispatchChain() {
return standardDispatchChain;
}
public DispatchChain getAsyncCreationDispatchChain() {
return asyncCreationDispatchChain;
}
}

View File

@ -1,34 +0,0 @@
// 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 com.cloud.api.dispatch;
import java.util.Map;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.ServerApiException;
public interface DispatchWorker {
/**
*
* @return false to stop the chain of responsibility, true
* to continue the chain with the next worker
*/
public boolean handle(BaseCmd cmd, Map<String, Object> params)
throws ServerApiException;
}

View File

@ -1,95 +0,0 @@
// 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 com.cloud.api.dispatch;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
/**
* This worker validates parameters in a generic way, by using annotated
* restrictions without involving the {@Link BaseCmd}. This worker doesn't
* know or care about the meaning of the parameters and that's why we can
* have it out of the {@Link BaseCmd}
*
* @author afornie
*/
public class ParamGenericValidationWorker implements DispatchWorker {
protected static Logger s_logger = Logger.getLogger(ParamGenericValidationWorker.class.getName());
protected static List<String> defaultParamNames = new ArrayList<String>();
static {
defaultParamNames.add(ApiConstants.CTX_START_EVENT_ID);
defaultParamNames.add(ApiConstants.COMMAND);
defaultParamNames.add(ApiConstants.USERNAME);
defaultParamNames.add(ApiConstants.USER_ID);
defaultParamNames.add(ApiConstants.PASSWORD);
defaultParamNames.add(ApiConstants.DOMAIN);
defaultParamNames.add(ApiConstants.DOMAIN_ID);
defaultParamNames.add(ApiConstants.DOMAIN__ID);
defaultParamNames.add(ApiConstants.SESSIONKEY);
defaultParamNames.add(ApiConstants.RESPONSE);
defaultParamNames.add(ApiConstants.PAGE);
defaultParamNames.add(ApiConstants.PAGE_SIZE);
defaultParamNames.add(ApiConstants.HTTPMETHOD);
defaultParamNames.add("_");
}
protected static final String ERROR_MSG_PREFIX = "Unknown parameters :";
@Override
public boolean handle(final BaseCmd cmd, final Map<String, Object> params) {
final List<String> expectedParamNames = getParamNamesForCommand(cmd);
final StringBuilder errorMsg = new StringBuilder(ERROR_MSG_PREFIX);
boolean foundUnknownParam = false;
for (final String paramName : params.keySet()) {
if (!expectedParamNames.contains(paramName)) {
errorMsg.append(" ").append(paramName);
foundUnknownParam= true;
}
}
if (foundUnknownParam) {
s_logger.warn(String.format("Received unkown parameters for command %s. %s", cmd.getCommandName(), errorMsg));
}
return true;
}
protected List<String> getParamNamesForCommand(final BaseCmd cmd) {
final List<String> paramNames = new ArrayList<String>();
// The expected param names are all the specific for the current command class ...
for (final Field field : cmd.getParamFields()) {
final Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
paramNames.add(parameterAnnotation.name());
}
// ... plus the default ones
paramNames.addAll(defaultParamNames);
return paramNames;
}
}

View File

@ -1,429 +0,0 @@
// 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 com.cloud.api.dispatch;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import javax.inject.Inject;
import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.InfrastructureEntity;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.EntityReference;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.BaseCmd.CommandType;
import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd;
import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd;
import org.apache.cloudstack.api.command.user.event.ArchiveEventsCmd;
import org.apache.cloudstack.api.command.user.event.DeleteEventsCmd;
import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
import org.apache.cloudstack.context.CallContext;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.utils.DateUtil;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.exception.CloudRuntimeException;
public class ParamProcessWorker implements DispatchWorker {
private static final Logger s_logger = Logger.getLogger(ParamProcessWorker.class.getName());
@Inject
protected AccountManager _accountMgr = null;
@Inject
protected EntityManager _entityMgr = null;
@Override
public boolean handle(final BaseCmd cmd, final Map<String, Object> params) {
processParameters(cmd, params);
return true;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public void processParameters(final BaseCmd cmd, final Map<String, Object> params) {
final Map<Object, AccessType> entitiesToAccess = new HashMap<Object, AccessType>();
final List<Field> cmdFields = cmd.getParamFields();
for (final Field field : cmdFields) {
final Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
final Object paramObj = params.get(parameterAnnotation.name());
if (paramObj == null) {
if (parameterAnnotation.required()) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " +
cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) +
" due to missing parameter " + parameterAnnotation.name());
}
continue;
}
// marshall the parameter into the correct type and set the field value
try {
setFieldValue(field, cmd, paramObj, parameterAnnotation);
} catch (final IllegalArgumentException argEx) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Unable to execute API command " + cmd.getCommandName() + " due to invalid value " + paramObj + " for parameter " +
parameterAnnotation.name());
}
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " +
cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to invalid value " + paramObj + " for parameter " +
parameterAnnotation.name());
} catch (final ParseException parseEx) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Invalid date parameter " + paramObj + " passed to command " + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8));
}
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to parse date " + paramObj + " for command " +
cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + ", please pass dates in the format mentioned in the api documentation");
} catch (final InvalidParameterValueException invEx) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " +
cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to invalid value. " + invEx.getMessage());
} catch (final CloudRuntimeException cloudEx) {
s_logger.error("CloudRuntimeException", cloudEx);
// FIXME: Better error message? This only happens if the API command is not executable, which typically
//means
// there was
// and IllegalAccessException setting one of the parameters.
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Internal error executing API command " +
cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8));
}
//check access on the resource this field points to
try {
final ACL checkAccess = field.getAnnotation(ACL.class);
final CommandType fieldType = parameterAnnotation.type();
if (checkAccess != null) {
// Verify that caller can perform actions in behalf of vm owner
//acumulate all Controlled Entities together.
//parse the array of resource types and in case of map check access on key or value or both as specified in @acl
//implement external dao for classes that need findByName
//for maps, specify access to be checkd on key or value.
// find the controlled entity DBid by uuid
if (parameterAnnotation.entityType() != null) {
final Class<?>[] entityList = parameterAnnotation.entityType()[0].getAnnotation(EntityReference.class).value();
for (final Class entity : entityList) {
// Check if the parameter type is a single
// Id or list of id's/name's
switch (fieldType) {
case LIST:
final CommandType listType = parameterAnnotation.collectionType();
switch (listType) {
case LONG:
case UUID:
final List<Long> listParam = (List<Long>)field.get(cmd);
for (final Long entityId : listParam) {
final Object entityObj = _entityMgr.findById(entity, entityId);
entitiesToAccess.put(entityObj, checkAccess.accessType());
}
break;
/*
* case STRING: List<String> listParam =
* new ArrayList<String>(); listParam =
* (List)field.get(cmd); for(String
* entityName: listParam){
* ControlledEntity entityObj =
* (ControlledEntity
* )daoClassInstance(entityId);
* entitiesToAccess.add(entityObj); }
* break;
*/
default:
break;
}
break;
case LONG:
case UUID:
final Object entityObj = _entityMgr.findById(entity, (Long)field.get(cmd));
entitiesToAccess.put(entityObj, checkAccess.accessType());
break;
default:
break;
}
if (ControlledEntity.class.isAssignableFrom(entity)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("ControlledEntity name is:" + entity.getName());
}
}
if (InfrastructureEntity.class.isAssignableFrom(entity)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("InfrastructureEntity name is:" + entity.getName());
}
}
}
}
}
} catch (final IllegalArgumentException e) {
s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() +
" is not accessible]");
} catch (final IllegalAccessException e) {
s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible.");
throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() +
" is not accessible]");
}
}
doAccessChecks(cmd, entitiesToAccess);
}
private void doAccessChecks(final BaseCmd cmd, final Map<Object, AccessType> entitiesToAccess) {
final Account caller = CallContext.current().getCallingAccount();
final Account owner = _accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
if (cmd instanceof BaseAsyncCreateCmd) {
//check that caller can access the owner account.
_accountMgr.checkAccess(caller, null, true, owner);
}
if (!entitiesToAccess.isEmpty()) {
//check that caller can access the owner account.
_accountMgr.checkAccess(caller, null, true, owner);
for (final Object entity : entitiesToAccess.keySet()) {
if (entity instanceof ControlledEntity) {
_accountMgr.checkAccess(caller, entitiesToAccess.get(entity), true, (ControlledEntity)entity);
} else if (entity instanceof InfrastructureEntity) {
//FIXME: Move this code in adapter, remove code from Account manager
}
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void setFieldValue(final Field field, final BaseCmd cmdObj, final Object paramObj, final Parameter annotation) throws IllegalArgumentException, ParseException {
try {
field.setAccessible(true);
final CommandType fieldType = annotation.type();
switch (fieldType) {
case BOOLEAN:
field.set(cmdObj, Boolean.valueOf(paramObj.toString()));
break;
case DATE:
// This piece of code is for maintaining backward compatibility
// and support both the date formats(Bug 9724)
// Do the date messaging for ListEventsCmd only
if (cmdObj instanceof ListEventsCmd || cmdObj instanceof DeleteEventsCmd || cmdObj instanceof ArchiveEventsCmd ||
cmdObj instanceof ArchiveAlertsCmd || cmdObj instanceof DeleteAlertsCmd) {
final boolean isObjInNewDateFormat = isObjInNewDateFormat(paramObj.toString());
if (isObjInNewDateFormat) {
final DateFormat newFormat = BaseCmd.NEW_INPUT_FORMAT;
synchronized (newFormat) {
field.set(cmdObj, newFormat.parse(paramObj.toString()));
}
} else {
final DateFormat format = BaseCmd.INPUT_FORMAT;
synchronized (format) {
Date date = format.parse(paramObj.toString());
if (field.getName().equals("startDate")) {
date = messageDate(date, 0, 0, 0);
} else if (field.getName().equals("endDate")) {
date = messageDate(date, 23, 59, 59);
}
field.set(cmdObj, date);
}
}
} else {
final DateFormat format = BaseCmd.INPUT_FORMAT;
format.setLenient(false);
synchronized (format) {
field.set(cmdObj, format.parse(paramObj.toString()));
}
}
break;
case FLOAT:
// Assuming that the parameters have been checked for required before now,
// we ignore blank or null values and defer to the command to set a default
// value for optional parameters ...
if (paramObj != null && isNotBlank(paramObj.toString())) {
field.set(cmdObj, Float.valueOf(paramObj.toString()));
}
break;
case INTEGER:
// Assuming that the parameters have been checked for required before now,
// we ignore blank or null values and defer to the command to set a default
// value for optional parameters ...
if (paramObj != null && isNotBlank(paramObj.toString())) {
field.set(cmdObj, Integer.valueOf(paramObj.toString()));
}
break;
case LIST:
final List listParam = new ArrayList();
final StringTokenizer st = new StringTokenizer(paramObj.toString(), ",");
while (st.hasMoreTokens()) {
final String token = st.nextToken();
final CommandType listType = annotation.collectionType();
switch (listType) {
case INTEGER:
listParam.add(Integer.valueOf(token));
break;
case UUID:
if (token.isEmpty())
break;
final Long internalId = translateUuidToInternalId(token, annotation);
listParam.add(internalId);
break;
case LONG: {
listParam.add(Long.valueOf(token));
}
break;
case SHORT:
listParam.add(Short.valueOf(token));
case STRING:
listParam.add(token);
break;
}
}
field.set(cmdObj, listParam);
break;
case UUID:
if (paramObj.toString().isEmpty())
break;
final Long internalId = translateUuidToInternalId(paramObj.toString(), annotation);
field.set(cmdObj, internalId);
break;
case LONG:
field.set(cmdObj, Long.valueOf(paramObj.toString()));
break;
case SHORT:
field.set(cmdObj, Short.valueOf(paramObj.toString()));
break;
case STRING:
if ((paramObj != null) && paramObj.toString().length() > annotation.length()) {
s_logger.error("Value greater than max allowed length " + annotation.length() + " for param: " + field.getName());
throw new InvalidParameterValueException("Value greater than max allowed length " + annotation.length() + " for param: " + field.getName());
}
field.set(cmdObj, paramObj.toString());
break;
case TZDATE:
field.set(cmdObj, DateUtil.parseTZDateString(paramObj.toString()));
break;
case MAP:
default:
field.set(cmdObj, paramObj);
break;
}
} catch (final IllegalAccessException ex) {
s_logger.error("Error initializing command " + cmdObj.getCommandName() + ", field " + field.getName() + " is not accessible.");
throw new CloudRuntimeException("Internal error initializing parameters for command " + cmdObj.getCommandName() + " [field " + field.getName() +
" is not accessible]");
}
}
private boolean isObjInNewDateFormat(final String string) {
final Matcher matcher = BaseCmd.newInputDateFormat.matcher(string);
return matcher.matches();
}
private Date messageDate(final Date date, final int hourOfDay, final int minute, final int second) {
final Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, second);
return cal.getTime();
}
private Long translateUuidToInternalId(final String uuid, final Parameter annotation) {
if (uuid.equals("-1")) {
// FIXME: This is to handle a lot of hardcoded special cases where -1 is sent
// APITODO: Find and get rid of all hardcoded params in API Cmds and service layer
return -1L;
}
Long internalId = null;
// If annotation's empty, the cmd existed before 3.x try conversion to long
final boolean isPre3x = annotation.since().isEmpty();
// Match against Java's UUID regex to check if input is uuid string
final boolean isUuid = uuid.matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
// Enforce that it's uuid for newly added apis from version 3.x
if (!isPre3x && !isUuid)
return null;
// Allow both uuid and internal id for pre3x apis
if (isPre3x && !isUuid) {
try {
internalId = Long.parseLong(uuid);
} catch (final NumberFormatException e) {
internalId = null;
}
if (internalId != null)
return internalId;
}
// There may be multiple entities defined on the @EntityReference of a Response.class
// UUID CommandType would expect only one entityType, so use the first entityType
final Class<?>[] entities = annotation.entityType()[0].getAnnotation(EntityReference.class).value();
// Go through each entity which is an interface to a VO class and get a VO object
// Try to getId() for the object using reflection, break on first non-null value
for (final Class<?> entity : entities) {
// For backward compatibility, we search within removed entities and let service layer deal
// with removed ones, return empty response or error
final Object objVO = _entityMgr.findByUuidIncludingRemoved(entity, uuid);
if (objVO == null) {
continue;
}
// Invoke the getId method, get the internal long ID
// If that fails hide exceptions as the uuid may not exist
try {
internalId = ((InternalIdentity)objVO).getId();
} catch (final IllegalArgumentException e) {
} catch (final NullPointerException e) {
}
// Return on first non-null Id for the uuid entity
if (internalId != null)
break;
}
if (internalId == null) {
if (s_logger.isDebugEnabled())
s_logger.debug("Object entity uuid = " + uuid + " does not exist in the database.");
throw new InvalidParameterValueException("Invalid parameter " + annotation.name() + " value=" + uuid +
" due to incorrect long value format, or entity does not exist or due to incorrect parameter annotation for the field in api cmd class.");
}
return internalId;
}
}

View File

@ -1,40 +0,0 @@
// 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 com.cloud.api.dispatch;
import java.util.Map;
import org.apache.cloudstack.api.BaseCmd;
/**
* This worker validates parameters in a semantic way, that is of
* course specific for each {@link BaseCmd}, so actually it delegates
* the validation on the {@link BaseCmd} itself
*
* @author afornie
*/
public class ParamSemanticValidationWorker implements DispatchWorker {
@Override
public boolean handle(final BaseCmd cmd, final Map<String, Object> params) {
cmd.validateSpecificParameters(params);
return true;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1362,7 +1362,8 @@ Listener, Configurable {
private final static int DEFAULT_PRIORITY = 100;
private final static int DEFAULT_DELTA = 2;
protected int getUpdatedPriority(final Network guestNetwork, final List<DomainRouterVO> routers, final DomainRouterVO exclude) throws InsufficientVirtualNetworkCapcityException {
protected int getUpdatedPriority(final Network guestNetwork, final List<DomainRouterVO> routers, final DomainRouterVO exclude)
throws InsufficientVirtualNetworkCapcityException {
int priority;
if (routers.size() == 0) {
priority = DEFAULT_PRIORITY;
@ -2564,7 +2565,8 @@ Listener, Configurable {
}
}
protected void finalizeIpAssocForNetwork(final Commands cmds, final VirtualRouter router, final Provider provider, final Long guestNetworkId, final Map<String, String> vlanMacAddress) {
protected void finalizeIpAssocForNetwork(final Commands cmds, final VirtualRouter router, final Provider provider, final Long guestNetworkId,
final Map<String, String> vlanMacAddress) {
final ArrayList<? extends PublicIpAddress> publicIps = getPublicIpsToApply(router, provider, guestNetworkId);
@ -2792,7 +2794,8 @@ Listener, Configurable {
}
@Override
public DomainRouterVO stop(final VirtualRouter router, final boolean forced, final User user, final Account caller) throws ConcurrentOperationException, ResourceUnavailableException {
public DomainRouterVO stop(final VirtualRouter router, final boolean forced, final User user, final Account caller) throws ConcurrentOperationException,
ResourceUnavailableException {
s_logger.debug("Stopping router " + router);
try {
_itMgr.advanceStop(router.getUuid(), forced);
@ -2973,7 +2976,8 @@ Listener, Configurable {
});
}
private void createDeleteIpAliasCommand(final DomainRouterVO router, final List<IpAliasTO> deleteIpAliasTOs, final List<IpAliasTO> createIpAliasTos, final long networkId, final Commands cmds) {
private void createDeleteIpAliasCommand(final DomainRouterVO router, final List<IpAliasTO> deleteIpAliasTOs, final List<IpAliasTO> createIpAliasTos, final long networkId,
final Commands cmds) {
final String routerip = getRouterIpInNetwork(networkId, router.getId());
final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId());
final DeleteIpAliasCommand deleteIpaliasCmd = new DeleteIpAliasCommand(routerip, deleteIpAliasTOs, createIpAliasTos);
@ -4160,7 +4164,7 @@ Listener, Configurable {
//Checks if the router is at the required version
// Compares MS version and router version
protected boolean checkRouterVersion(final VirtualRouter router){
protected boolean checkRouterVersion(final VirtualRouter router) {
if(!routerVersionCheckEnabled.value()){
//Router version check is disabled.
return true;

View File

@ -83,8 +83,6 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
protected VolumeDao _volsDao;
@Inject
protected ConfigurationDao _configDao;
@Inject
protected ApiDispatcher _dispatcher;
protected AsyncJobDispatcher _asyncDispatcher;
@ -98,21 +96,21 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
return _asyncDispatcher;
}
public void setAsyncJobDispatcher(final AsyncJobDispatcher dispatcher) {
public void setAsyncJobDispatcher(AsyncJobDispatcher dispatcher) {
_asyncDispatcher = dispatcher;
}
private Date getNextScheduledTime(final long policyId, final Date currentTimestamp) {
final SnapshotPolicyVO policy = _snapshotPolicyDao.findById(policyId);
private Date getNextScheduledTime(long policyId, Date currentTimestamp) {
SnapshotPolicyVO policy = _snapshotPolicyDao.findById(policyId);
Date nextTimestamp = null;
if (policy != null) {
final short intervalType = policy.getInterval();
final IntervalType type = DateUtil.getIntervalType(intervalType);
final String schedule = policy.getSchedule();
final String timezone = policy.getTimezone();
short intervalType = policy.getInterval();
IntervalType type = DateUtil.getIntervalType(intervalType);
String schedule = policy.getSchedule();
String timezone = policy.getTimezone();
nextTimestamp = DateUtil.getNextRunTime(type, schedule, timezone, currentTimestamp);
final String currentTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp);
final String nextScheduledTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, nextTimestamp);
String currentTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp);
String nextScheduledTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, nextTimestamp);
s_logger.debug("Current time is " + currentTime + ". NextScheduledTime of policyId " + policyId + " is " + nextScheduledTime);
}
return nextTimestamp;
@ -122,7 +120,7 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
* {@inheritDoc}
*/
@Override
public void poll(final Date currentTimestamp) {
public void poll(Date currentTimestamp) {
// We don't maintain the time. The timer task does.
_currentTimestamp = currentTimestamp;
@ -154,12 +152,12 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
}
private void checkStatusOfCurrentlyExecutingSnapshots() {
final SearchCriteria<SnapshotScheduleVO> sc = _snapshotScheduleDao.createSearchCriteria();
SearchCriteria<SnapshotScheduleVO> sc = _snapshotScheduleDao.createSearchCriteria();
sc.addAnd("asyncJobId", SearchCriteria.Op.NNULL);
final List<SnapshotScheduleVO> snapshotSchedules = _snapshotScheduleDao.search(sc, null);
for (final SnapshotScheduleVO snapshotSchedule : snapshotSchedules) {
final Long asyncJobId = snapshotSchedule.getAsyncJobId();
final AsyncJobVO asyncJob = _asyncJobDao.findById(asyncJobId);
List<SnapshotScheduleVO> snapshotSchedules = _snapshotScheduleDao.search(sc, null);
for (SnapshotScheduleVO snapshotSchedule : snapshotSchedules) {
Long asyncJobId = snapshotSchedule.getAsyncJobId();
AsyncJobVO asyncJob = _asyncJobDao.findById(asyncJobId);
switch (asyncJob.getStatus()) {
case SUCCEEDED:
// The snapshot has been successfully backed up.
@ -170,7 +168,7 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
break;
case FAILED:
// Check the snapshot status.
final Long snapshotId = snapshotSchedule.getSnapshotId();
Long snapshotId = snapshotSchedule.getSnapshotId();
if (snapshotId == null) {
// createSnapshotAsync exited, successfully or unsuccessfully,
// even before creating a snapshot record
@ -178,7 +176,7 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
// Schedule the next snapshot.
scheduleNextSnapshotJob(snapshotSchedule);
} else {
final SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
if (snapshot == null || snapshot.getRemoved() != null) {
// This snapshot has been deleted successfully from the primary storage
// Again no cleanup needs to be done.
@ -222,16 +220,16 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, _currentTimestamp);
s_logger.debug("Snapshot scheduler.poll is being called at " + displayTime);
final List<SnapshotScheduleVO> snapshotsToBeExecuted = _snapshotScheduleDao.getSchedulesToExecute(_currentTimestamp);
List<SnapshotScheduleVO> snapshotsToBeExecuted = _snapshotScheduleDao.getSchedulesToExecute(_currentTimestamp);
s_logger.debug("Got " + snapshotsToBeExecuted.size() + " snapshots to be executed at " + displayTime);
for (final SnapshotScheduleVO snapshotToBeExecuted : snapshotsToBeExecuted) {
for (SnapshotScheduleVO snapshotToBeExecuted : snapshotsToBeExecuted) {
SnapshotScheduleVO tmpSnapshotScheduleVO = null;
final long snapshotScheId = snapshotToBeExecuted.getId();
final long policyId = snapshotToBeExecuted.getPolicyId();
final long volumeId = snapshotToBeExecuted.getVolumeId();
long snapshotScheId = snapshotToBeExecuted.getId();
long policyId = snapshotToBeExecuted.getPolicyId();
long volumeId = snapshotToBeExecuted.getVolumeId();
try {
final VolumeVO volume = _volsDao.findById(volumeId);
VolumeVO volume = _volsDao.findById(volumeId);
if (volume.getPoolId() == null) {
// this volume is not attached
continue;
@ -240,41 +238,40 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
_snapshotScheduleDao.remove(snapshotToBeExecuted.getId());
}
if (s_logger.isDebugEnabled()) {
final Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp();
Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp();
displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
s_logger.debug("Scheduling 1 snapshot for volume " + volumeId + " for schedule id: " + snapshotToBeExecuted.getId() + " at " + displayTime);
}
tmpSnapshotScheduleVO = _snapshotScheduleDao.acquireInLockTable(snapshotScheId);
final Long eventId =
Long eventId =
ActionEventUtils.onScheduledActionEvent(User.UID_SYSTEM, volume.getAccountId(), EventTypes.EVENT_SNAPSHOT_CREATE, "creating snapshot for volume Id:" +
volumeId, 0);
final Map<String, Object> params = new HashMap<String, Object>();
Map<String, String> params = new HashMap<String, String>();
params.put(ApiConstants.VOLUME_ID, "" + volumeId);
params.put(ApiConstants.POLICY_ID, "" + policyId);
params.put("ctxUserId", "1");
params.put("ctxAccountId", "" + volume.getAccountId());
params.put("ctxStartEventId", String.valueOf(eventId));
final CreateSnapshotCmd cmd = new CreateSnapshotCmd();
CreateSnapshotCmd cmd = new CreateSnapshotCmd();
ComponentContext.inject(cmd);
_dispatcher.dispatchCreateCmd(cmd, params);
ApiDispatcher.getInstance().dispatchCreateCmd(cmd, params);
params.put("id", "" + cmd.getEntityId());
params.put("ctxStartEventId", "1");
final AsyncJobVO job =
AsyncJobVO job =
new AsyncJobVO(UUID.randomUUID().toString(), User.UID_SYSTEM, volume.getAccountId(), CreateSnapshotCmd.class.getName(), ApiGsonHelper.getBuilder()
.create()
.toJson(params), cmd.getEntityId(), cmd.getInstanceType() != null ? cmd.getInstanceType().toString() : null);
job.setDispatcher(_asyncDispatcher.getName());
final long jobId = _asyncMgr.submitAsyncJob(job);
long jobId = _asyncMgr.submitAsyncJob(job);
tmpSnapshotScheduleVO.setAsyncJobId(jobId);
_snapshotScheduleDao.update(snapshotScheId, tmpSnapshotScheduleVO);
} catch (final Exception e) {
// TODO Logging this exception is enough?
} catch (Exception e) {
s_logger.warn("Scheduling snapshot failed due to " + e.toString());
} finally {
if (tmpSnapshotScheduleVO != null) {
@ -284,16 +281,16 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
}
}
private Date scheduleNextSnapshotJob(final SnapshotScheduleVO snapshotSchedule) {
private Date scheduleNextSnapshotJob(SnapshotScheduleVO snapshotSchedule) {
if (snapshotSchedule == null) {
return null;
}
final Long policyId = snapshotSchedule.getPolicyId();
Long policyId = snapshotSchedule.getPolicyId();
if (policyId.longValue() == Snapshot.MANUAL_POLICY_ID) {
// Don't need to schedule the next job for this.
return null;
}
final SnapshotPolicyVO snapshotPolicy = _snapshotPolicyDao.findById(policyId);
SnapshotPolicyVO snapshotPolicy = _snapshotPolicyDao.findById(policyId);
if (snapshotPolicy == null) {
_snapshotScheduleDao.expunge(snapshotSchedule.getId());
}
@ -302,15 +299,15 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
@Override
@DB
public Date scheduleNextSnapshotJob(final SnapshotPolicyVO policy) {
public Date scheduleNextSnapshotJob(SnapshotPolicyVO policy) {
if (policy == null) {
return null;
}
final long policyId = policy.getId();
long policyId = policy.getId();
if (policyId == Snapshot.MANUAL_POLICY_ID) {
return null;
}
final Date nextSnapshotTimestamp = getNextScheduledTime(policyId, _currentTimestamp);
Date nextSnapshotTimestamp = getNextScheduledTime(policyId, _currentTimestamp);
SnapshotScheduleVO spstSchedVO = _snapshotScheduleDao.findOneByVolumePolicy(policy.getVolumeId(), policy.getId());
if (spstSchedVO == null) {
spstSchedVO = new SnapshotScheduleVO(policy.getVolumeId(), policyId, nextSnapshotTimestamp);
@ -334,9 +331,9 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
@Override
@DB
public boolean removeSchedule(final Long volumeId, final Long policyId) {
public boolean removeSchedule(Long volumeId, Long policyId) {
// We can only remove schedules which are in the future. Not which are already executed in the past.
final SnapshotScheduleVO schedule = _snapshotScheduleDao.getCurrentSchedule(volumeId, policyId, false);
SnapshotScheduleVO schedule = _snapshotScheduleDao.getCurrentSchedule(volumeId, policyId, false);
boolean success = true;
if (schedule != null) {
success = _snapshotScheduleDao.remove(schedule.getId());
@ -348,18 +345,18 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
}
@Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
_snapshotPollInterval = NumbersUtil.parseInt(_configDao.getValue("snapshot.poll.interval"), 300);
final boolean snapshotsRecurringTest = Boolean.parseBoolean(_configDao.getValue("snapshot.recurring.test"));
boolean snapshotsRecurringTest = Boolean.parseBoolean(_configDao.getValue("snapshot.recurring.test"));
if (snapshotsRecurringTest) {
// look for some test values in the configuration table so that snapshots can be taken more frequently (QA test code)
final int minutesPerHour = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.minutes.per.hour"), 60);
final int hoursPerDay = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.hours.per.day"), 24);
final int daysPerWeek = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.days.per.week"), 7);
final int daysPerMonth = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.days.per.month"), 30);
final int weeksPerMonth = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.weeks.per.month"), 4);
final int monthsPerYear = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.months.per.year"), 12);
int minutesPerHour = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.minutes.per.hour"), 60);
int hoursPerDay = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.hours.per.day"), 24);
int daysPerWeek = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.days.per.week"), 7);
int daysPerMonth = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.days.per.month"), 30);
int weeksPerMonth = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.weeks.per.month"), 4);
int monthsPerYear = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.months.per.year"), 12);
_testTimerTask = new TestClock(this, minutesPerHour, hoursPerDay, daysPerWeek, daysPerMonth, weeksPerMonth, monthsPerYear);
}
@ -374,8 +371,8 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
@DB
public boolean start() {
// reschedule all policies after management restart
final List<SnapshotPolicyVO> policyInstances = _snapshotPolicyDao.listAll();
for (final SnapshotPolicyVO policyInstance : policyInstances) {
List<SnapshotPolicyVO> policyInstances = _snapshotPolicyDao.listAll();
for (SnapshotPolicyVO policyInstance : policyInstances) {
if (policyInstance.getId() != Snapshot.MANUAL_POLICY_ID) {
scheduleNextSnapshotJob(policyInstance);
}
@ -386,13 +383,13 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
// Else it becomes too confusing.
_testClockTimer.schedule(_testTimerTask, 100 * 1000L, 60 * 1000L);
} else {
final TimerTask timerTask = new ManagedContextTimerTask() {
TimerTask timerTask = new ManagedContextTimerTask() {
@Override
protected void runInContext() {
try {
final Date currentTimestamp = new Date();
Date currentTimestamp = new Date();
poll(currentTimestamp);
} catch (final Throwable t) {
} catch (Throwable t) {
s_logger.warn("Catch throwable in snapshot scheduler ", t);
}
}

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package com.cloud.api.dispatch;
package com.cloud.api;
import java.util.HashMap;
@ -44,12 +44,10 @@ import com.cloud.user.AccountManager;
import com.cloud.user.User;
@RunWith(MockitoJUnitRunner.class)
public class ParamProcessWorkerTest {
public class ApiDispatcherTest {
@Mock
protected AccountManager accountManager;
protected ParamProcessWorker paramProcessWorker;
AccountManager accountManager;
public static class TestCmd extends BaseCmd {
@ -83,8 +81,8 @@ public class ParamProcessWorkerTest {
@Before
public void setup() {
CallContext.register(Mockito.mock(User.class), Mockito.mock(Account.class));
paramProcessWorker = new ParamProcessWorker();
paramProcessWorker._accountMgr = accountManager;
new ApiDispatcher().init();
ApiDispatcher.getInstance()._accountMgr = accountManager;
}
@After
@ -94,12 +92,13 @@ public class ParamProcessWorkerTest {
@Test
public void processParameters() {
final HashMap<String, Object> params = new HashMap<String, Object>();
HashMap<String, String> params = new HashMap<String, String>();
params.put("strparam1", "foo");
params.put("intparam1", "100");
params.put("boolparam1", "true");
final TestCmd cmd = new TestCmd();
paramProcessWorker.processParameters(cmd, params);
TestCmd cmd = new TestCmd();
//how lucky that field is not protected, this test would be impossible
ApiDispatcher.processParameters(cmd, params);
Assert.assertEquals("foo", cmd.strparam1);
Assert.assertEquals(100, cmd.intparam1);
}

View File

@ -1,48 +0,0 @@
// 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 com.cloud.api.dispatch;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
import com.cloud.exception.ResourceAllocationException;
public class CommandCreationWorkerTest {
@Test
public void testHandle() throws ResourceAllocationException {
// Prepare
final BaseAsyncCreateCmd asyncCreateCmd = mock(BaseAsyncCreateCmd.class);
final Map<String, Object> params = new HashMap<String, Object>();
// Execute
final CommandCreationWorker creationWorker = new CommandCreationWorker();
creationWorker.handle(asyncCreateCmd, params);
// Assert
verify(asyncCreateCmd, times(1)).create();
}
}

View File

@ -1,54 +0,0 @@
// 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 com.cloud.api.dispatch;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class DispatchChainFactoryTest {
protected static final String STANDARD_CHAIN_ERROR = "Expecting worker of class %s at index %s of StandardChain";
protected static final String ASYNC_CHAIN_ERROR = "Expecting worker of class %s at index %s of StandardChain";
@Test
public void testAllChainCreation() {
// Prepare
final DispatchChainFactory dispatchChainFactory = new DispatchChainFactory();
dispatchChainFactory.paramGenericValidationWorker = new ParamGenericValidationWorker();
dispatchChainFactory.paramSemanticValidationWorker = new ParamSemanticValidationWorker();
dispatchChainFactory.paramProcessWorker = new ParamProcessWorker();
dispatchChainFactory.commandCreationWorker = new CommandCreationWorker();
final Class<?>[] standardClasses = {ParamGenericValidationWorker.class, ParamProcessWorker.class,
ParamSemanticValidationWorker.class};
final Class<?>[] asyncClasses = {ParamGenericValidationWorker.class, ParamProcessWorker.class,
ParamSemanticValidationWorker.class, CommandCreationWorker.class};
// Execute
dispatchChainFactory.setup();
final DispatchChain standardChain = dispatchChainFactory.getStandardDispatchChain();
final DispatchChain asyncChain = dispatchChainFactory.getAsyncCreationDispatchChain();
for (int i = 0; i < standardClasses.length; i++) {
assertEquals(String.format(STANDARD_CHAIN_ERROR, standardClasses[i], i),
standardClasses[i], standardChain.workers.get(i).getClass());
}
for (int i = 0; i < asyncClasses.length; i++) {
assertEquals(String.format(ASYNC_CHAIN_ERROR, asyncClasses[i], i),
asyncClasses[i], asyncChain.workers.get(i).getClass());
}
}
}

View File

@ -1,163 +0,0 @@
// 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 com.cloud.api.dispatch;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.user.Account;
import org.apache.log4j.Logger;
public class ParamGenericValidationWorkerTest {
protected String loggerOutput = null;
protected void driveTest(final BaseCmd cmd, final Map<String, Object> params) {
final ParamGenericValidationWorker genValidationWorker = new ParamGenericValidationWorker();
// We create a mock logger to verify the result
ParamGenericValidationWorker.s_logger = new Logger(""){
@Override
public void warn(final Object msg){
loggerOutput = msg.toString();
}
};
// Execute
genValidationWorker.handle(cmd, params);
}
@Test
public void testHandle() throws ResourceAllocationException {
// Prepare
final BaseCmd cmd = new FakeCmd();
final Map<String, Object> params = new HashMap<String, Object>();
params.put(ApiConstants.COMMAND, "");
params.put("addedParam", "");
// Execute
driveTest(cmd, params);
// Assert
assertEquals("There should be no errors since there are no unknown parameters for this command class", null, loggerOutput);
}
@Test
public void testHandleWithUnknownParams() throws ResourceAllocationException {
// Prepare
final String unknownParamKey = "unknownParam";
final BaseCmd cmd = new FakeCmd();
final Map<String, Object> params = new HashMap<String, Object>();
params.put(ApiConstants.COMMAND, "");
params.put("addedParam", "");
params.put(unknownParamKey, "");
// Execute
driveTest(cmd, params);
// Assert
assertTrue("There should be error msg, since there is one unknown parameter", loggerOutput.contains(unknownParamKey));
}
@Test
public void testHandleWithoutAuthorization() throws ResourceAllocationException {
final short type = 2;
driveAuthTest(type);
// Assert
assertTrue("There should be error msg, since there is one unauthorized parameter", loggerOutput.contains("paramWithRole"));
}
@Test
public void testHandleWithAuthorization() throws ResourceAllocationException {
final short type = 1;
driveAuthTest(type);
// Assert
assertEquals("There should be no errors since parameters have authorization", null, loggerOutput);
}
protected void driveAuthTest(final short type) {
// Prepare
final BaseCmd cmd = new FakeCmdWithRoleAdmin();
final Account account = mock(Account.class);
((FakeCmdWithRoleAdmin)cmd).account = account;
when(account.getType()).thenReturn(type);
final Map<String, Object> params = new HashMap<String, Object>();
params.put(ApiConstants.COMMAND, "");
params.put("addedParam", "");
params.put("paramWithRole", "");
// Execute
driveTest(cmd, params);
}
}
class FakeCmd extends BaseCmd {
@Parameter(name = "addedParam")
private String addedParam;
public Account account;
@Override
protected Account getCurrentContextAccount() {
return account;
}
//
// Dummy methods for mere correct compilation
//
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException,
NetworkRuleConflictException {
}
@Override
public String getCommandName() {
return null;
}
@Override
public long getEntityOwnerId() {
return 0;
}
}
class FakeCmdWithRoleAdmin extends FakeCmd {
@Parameter(name = "paramWithRole", authorized = {RoleType.Admin})
private String paramWithRole;
}

View File

@ -1,48 +0,0 @@
// 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 com.cloud.api.dispatch;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.apache.cloudstack.api.BaseCmd;
import com.cloud.exception.ResourceAllocationException;
public class ParamSemanticValidationWorkerTest {
@Test
public void testHandle() throws ResourceAllocationException {
// Prepare
final BaseCmd cmd = mock(BaseCmd.class);
final Map<String, Object> params = new HashMap<String, Object>();
// Execute
final ParamSemanticValidationWorker semanticValWorker = new ParamSemanticValidationWorker();
semanticValWorker.handle(cmd, params);
// Assert
verify(cmd, times(1)).validateSpecificParameters(params);
}
}