content: // 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.Map;
  
  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.SecurityChecker.AccessType;
  import org.apache.cloudstack.api.APICommand;
  import org.apache.cloudstack.api.ApiCommandResourceType;
  import org.apache.cloudstack.api.ApiConstants;
  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.BaseCustomIdCmd;
  import org.apache.cloudstack.context.CallContext;
  import org.apache.cloudstack.framework.jobs.AsyncJob;
  import org.apache.cloudstack.framework.jobs.AsyncJobManager;
  import org.apache.cloudstack.framework.jobs.impl.AsyncJobManagerImpl;
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  
  import com.cloud.api.dispatch.DispatchChain;
  import com.cloud.api.dispatch.DispatchChainFactory;
  import com.cloud.api.dispatch.DispatchTask;
  import com.cloud.projects.Project;
  import com.cloud.user.Account;
  import com.cloud.user.AccountManager;
  import com.cloud.utils.db.EntityManager;
  import com.cloud.utils.exception.CloudRuntimeException;
  
  public class ApiDispatcher {
      protected Logger logger = LogManager.getLogger(getClass());
  
      Long _createSnapshotQueueSizeLimit;
      Long migrateQueueSizeLimit;
  
      @Inject
      AsyncJobManager _asyncMgr;
  
      @Inject
      AccountManager _accountMgr;
  
      @Inject
      EntityManager _entityMgr;
  
      @Inject()
      protected DispatchChainFactory dispatchChainFactory;
  
      @Inject
      AsyncJobManagerImpl asyncJobManager;
  
      protected DispatchChain standardDispatchChain;
  
      protected DispatchChain asyncCreationDispatchChain;
  
      public ApiDispatcher() {
      }
  
      @PostConstruct
      public void setup() {
          standardDispatchChain = dispatchChainFactory.getStandardDispatchChain();
          asyncCreationDispatchChain = dispatchChainFactory.getAsyncCreationDispatchChain();
      }
  
      public void setCreateSnapshotQueueSizeLimit(final Long snapshotLimit) {
          _createSnapshotQueueSizeLimit = snapshotLimit;
      }
  
      public void setMigrateQueueSizeLimit(final Long migrateLimit) {
          migrateQueueSizeLimit = migrateLimit;
      }
  
      public void dispatchCreateCmd(final BaseAsyncCreateCmd cmd, final Map<String, String> params) throws Exception {
          if (asyncJobManager.isAsyncJobsEnabled()) {
              asyncCreationDispatchChain.dispatch(new DispatchTask(cmd, params));
          } else {
              throw new CloudRuntimeException("Maintenance or Shutdown has been initiated on this management server. Can not accept new async creation jobs");
          }
      }
  
      private void doAccessChecks(BaseCmd cmd, Map<Object, AccessType> entitiesToAccess) {
          Account caller = CallContext.current().getCallingAccount();
  
          APICommand commandAnnotation = cmd.getClass().getAnnotation(APICommand.class);
          String apiName = commandAnnotation != null ? commandAnnotation.name() : null;
  
          if (!entitiesToAccess.isEmpty()) {
              for (Object entity : entitiesToAccess.keySet()) {
                  if (entity instanceof ControlledEntity) {
                      _accountMgr.checkAccess(caller, entitiesToAccess.get(entity), false, apiName, (ControlledEntity) entity);
                  } else if (entity instanceof InfrastructureEntity) {
                      //FIXME: Move this code in adapter, remove code from Account manager
                  }
              }
          }
      }
  
      public void dispatch(final BaseCmd cmd, final Map<String, String> params, final boolean execute) throws Exception {
          // Let the chain of responsibility dispatch gradually
          standardDispatchChain.dispatch(new DispatchTask(cmd, params));
  
          final CallContext ctx = CallContext.current();
          ctx.setEventDisplayEnabled(cmd.isDisplay());
          if(params.get(ApiConstants.PROJECT_ID) != null) {
              Project project = _entityMgr.findByUuidIncludingRemoved(Project.class, params.get(ApiConstants.PROJECT_ID));
              ctx.setProject(project);
          }
          if (cmd.getApiResourceId() != null) {
              ctx.setEventResourceId(cmd.getApiResourceId());
          }
          final ApiCommandResourceType resourceType = cmd.getApiResourceType();
          if (resourceType != null && !ApiCommandResourceType.None.equals(resourceType)) {
              ctx.setEventResourceType(resourceType);
          }
  
          // TODO This if shouldn't be here. Use polymorphism and move it to validateSpecificParameters
          if (cmd instanceof BaseAsyncCmd) {
  
              final BaseAsyncCmd asyncCmd = (BaseAsyncCmd)cmd;
              final String startEventId = params.get(ApiConstants.CTX_START_EVENT_ID);
              ctx.setStartEventId(Long.parseLong(startEventId));
  
              // Synchronise job on the object if needed
              if (asyncCmd.getJob() != null && asyncCmd.getSyncObjId() != null && asyncCmd.getSyncObjType() != null) {
                  Long queueSizeLimit = null;
                  if (asyncCmd.getSyncObjType() != null && asyncCmd.getSyncObjType().equalsIgnoreCase(BaseAsyncCmd.snapshotHostSyncObject)) {
                          queueSizeLimit = _createSnapshotQueueSizeLimit;
                  } else if (asyncCmd.getSyncObjType() != null && asyncCmd.getSyncObjType().equalsIgnoreCase(BaseAsyncCmd.migrationSyncObject)) {
                          queueSizeLimit = migrateQueueSizeLimit;
                  } else {
                      queueSizeLimit = 1L;
                  }
  
                  if (queueSizeLimit != null) {
                      if (!execute) {
                          // if we are not within async-execution context, enqueue the command
                          _asyncMgr.syncAsyncJobExecution((AsyncJob)asyncCmd.getJob(), asyncCmd.getSyncObjType(), asyncCmd.getSyncObjId().longValue(), queueSizeLimit);
                          return;
                      }
                  } else {
                      logger.trace("The queue size is unlimited, skipping the synchronizing");
                  }
              }
          }
  
          // TODO This if shouldn't be here. Use polymorphism and move it to validateSpecificParameters
          if (cmd instanceof BaseAsyncCustomIdCmd) {
              ((BaseAsyncCustomIdCmd)cmd).checkUuid();
          } else if (cmd instanceof BaseCustomIdCmd) {
              ((BaseCustomIdCmd)cmd).checkUuid();
          }
  
          cmd.execute();
      }
  
  }
  