Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

Code Block
 public abstract class BaseCmd {
      static public ConfigurationService _configService;
      static public AccountService _accountService;
      static public UserVmService _userVmService;
      static public ManagementService _mgr;
     
    // more code ...
     
    // code to resolve above static references using runtime resolution
}

...

Code Block
public class ConfigurationDaoImpl extends GenericDaoBase<ConfigurationVO, String> implements ConfigurationDao {

	@PostConstruct @PostConstruct 	void initComponent() {		// more code...	}  
}

4.4

...

When to use or avoid creating run-time relationship with component container

Although we've tried the best to cut the relationship to a component container inside component code, we still have very few places in framework components that need to be aware of existence of the component container, to avoid any strong binding to a particular container like Spring, we introduced a class called ComponentContext, it is responsible to bridge CloudStack component with a chosen component container (for now, it is Spring). 

As a business logic component developer, you should avoid using any of the functions provided by ComponentContext, this can make the business component neutral to component container, since we use standard javax @Inject auto-wiring mechanism, it is even possible for your component to run in containers that are other than CloudStack.

4.5 CloudStack Customized AOP (Aspect-Oriented Programming)

limit direct use of ComponentContext with one exception: using ComponentContext.inject() to auto-wire components. Following example gives a scenario.

Code Block
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);

    ...
}


public class FooCmd {


    @Inject FooService _fooService;
    @Inject GeniusService _geniusService;


    ....


}

cmdObj = ComponentContext.inject(cmdObj);
Above one-line enabler makes it possible to use @Inject pattern in all CloudStack command classes consistently across the board. Spring will automatically wire the object in reference for the newly constructed object at run-time.  If you have to use run-time constructed objects and have cases to pass over a set of service objects, instead of using pattern like

Code Block
public class FooObject {

    FooService _fooService;
    
    public FooObject(FooService service) {
        _fooService = service;
    }



    ....
}

You may take advantage of auto-wiring as

Code Block
public class FooObject {
    @Inject FooService _fooService;

    public FooObject() {
    }
    ...
}

If you see yourself constantly need to pass a lot of service objects to a object constructor (or setters), use ComponentContext.inject() to help you out.

4.5 CloudStack Customized AOP (Aspect-Oriented Programming)

We found that outWe found that out-of-box AOP offering from Spring does not work with CloudStack, Spring AOP uses proxy mechanism, it can only intercept method calls that are "calling into" the component through a generated proxy object, unfortunately some of CloudStack codebase relies on the fact that method interception should happen for inner method calls within the component class itself. To solve this problem, we have to develop a customized AOP under Spring. Following is an example of such use case.

Code Block
package com.cloud.event;

import java.lang.reflect.Method;

import org.apache.log4j.Logger;

import com.cloud.user.UserContext;
import com.cloud.utils.component.ComponentMethodInterceptor;

public class ActionEventInterceptor implements ComponentMethodInterceptor {
    private static final Logger s_logger = Logger.getLogger(ActionEventInterceptor.class);

    public ActionEventInterceptor() {
    }

    @Override
    public Object interceptStart(Method method, Object target) {
        EventVO event = null;
        ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
        if (actionEvent != null) {
            boolean async = actionEvent.async();
            if(async){
                UserContext ctx = UserContext.current();
                long userId = ctx.getCallerUserId();
                long accountId = ctx.getAccountId();
                long startEventId = ctx.getStartEventId();
                String eventDescription = actionEvent.eventDescription();
                if(ctx.getEventDetails() != null){
                    eventDescription += ". "+ctx.getEventDetails();
                }
                EventUtils.saveStartedEvent(userId, accountId, actionEvent.eventType(), eventDescription, startEventId);
            }
        }
        return event;
    }

    @Override
    public void interceptComplete(Method method, Object target, Object event) {
        ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
        if (actionEvent != null) {
            UserContext ctx = UserContext.current();
            long userId = ctx.getCallerUserId();
            long accountId = ctx.getAccountId();
            long startEventId = ctx.getStartEventId();
            String eventDescription = actionEvent.eventDescription();();
            if(ctx.getEventDetails() != null){
                eventDescription += ". "+ctx.getEventDetails();
            }
            if(ctxactionEvent.getEventDetailscreate() != null){
                //This  eventDescription += ". "+ctx.getEventDetails();
start event has to be used for subsequent events of this action
             }
   startEventId = EventUtils.saveCreatedEvent(userId, accountId, EventVO.LEVEL_INFO, actionEvent.eventType(), "Successfully created entity  if(actionEvent.create()){for "+eventDescription);
                //This start event has to be used for subsequent events of this action
  ctx.setStartEventId(startEventId);
            } else {
              startEventId = EventUtils.saveCreatedEventsaveEvent(userId, accountId, EventVO.LEVEL_INFO, actionEvent.eventType(), "Successfully completed "+eventDescription, "Successfully created entity for "+eventDescription); startEventId);
            }
        }
    }

    @Override
    public void interceptException(Method method, Object target, Object  ctx.setStartEventId(startEventId);event) {
        ActionEvent actionEvent =  } else {
method.getAnnotation(ActionEvent.class);
        if (actionEvent != null) {
        EventUtils.saveEvent(userId, accountId, EventVO.LEVEL_INFO, actionEvent.eventType(), "Successfully completed "+eventDescription, startEventId UserContext ctx = UserContext.current();
            }
long userId = ctx.getCallerUserId();
         }
   long accountId  }
= ctx.getAccountId();
    @Override
    public void interceptException(Method method, Objectlong target,startEventId Object event) {
= ctx.getStartEventId();
            ActionEventString actionEventeventDescription = methodactionEvent.getAnnotationeventDescription(ActionEvent.class);
        if (actionEvent    if(ctx.getEventDetails() != null) {
            UserContext    ctxeventDescription += UserContext.current ". "+ctx.getEventDetails();
            }
   long userId = ctx.getCallerUserId();
         if(actionEvent.create()){
                long eventId = EventUtils.saveCreatedEvent(userId, accountId = ctx.getAccountId();
            long startEventId = ctx.getStartEventId();
, EventVO.LEVEL_ERROR, actionEvent.eventType(), "Error while creating entity for "+eventDescription);
             String eventDescription = actionEventctx.eventDescriptionsetStartEventId(eventId);
            if(ctx.getEventDetails() != null)} else {
                EventUtils.saveEvent(userId,  eventDescription += ". "+ctx.getEventDetails(accountId, EventVO.LEVEL_ERROR, actionEvent.eventType(), "Error while "+eventDescription, startEventId);
            }
        }
    if(actionEvent.create()){}

    @Override
    public boolean needToIntercept(Method method) {
        longActionEvent eventIdactionEvent = EventUtilsmethod.saveCreatedEvent(userId, accountId, EventVO.LEVEL_ERROR, actionEvent.eventType(), "Error while creating entity for "+eventDescription);
   getAnnotation(ActionEvent.class);
        if (actionEvent != null) {
             ctx.setStartEventId(eventId)return true;
            } else {

        return false;
       EventUtils.saveEvent(userId, accountId, EventVO.LEVEL_ERROR, actionEvent.eventType(), "Error while "+eventDescription, startEventId);
            }
        }
    }

    @Override
    public boolean needToIntercept(Method method) {}
}

If you ever need to implement a method interceptor, implement interface ComponentMethodInterceptor and declare it in applicationContext.xml.in. As you see in the above example, ActionEventInterceptor implements an aspect to automatically log events at methods in classes that have been marked as @ActionEvent. To install such interceptor, declare it as following in applicationContext.xml.in 

Code Block

  <bean id="actionEventInterceptor" class="com.cloud.event.ActionEventInterceptor" />

  <bean id="instantiatePostProcessor" class="com.cloud.utils.component.ComponentInstantiationPostProcessor">
    <property name="Interceptors">
        ActionEvent<list>
 actionEvent = method.getAnnotation(ActionEvent.class);
        if (actionEvent<ref !bean= null) {"transactionContextBuilder" />
            return true; <ref bean="actionEventInterceptor" />
        }</list>

    </property>
    return false;
    }
}

If you ever need to implement a method interceptor, implement interface ComponentMethodInterceptor and declare it in applicationContext.xml.in. 

...

</bean>

Not any class can be intercepted in this way, both due to implementation limitation and for sake of performance, only classes that have marked itself with a marker interface ComponentMethodInterceptable can be intercepted to apply above AOP pattern. All Manager, Adapter and DAO classes are currently marked with ComponentMethodInterceptable.

ComponentInstantiationPostProcessor is a CloudStack implemented Spring post processor that eventually implements the Customized AOP pattern. ComponentInstantiationPostProcessor uses CGLIB to replace loaded component instance with a CGLIB generated wrapper object that is a sub-class instance of the original object, in this way, it can provide hooks to all method calls to the object through ComponentMethodInterceptor.

The sub-class generated by CGLIB uses the naming convention implemented in ComponentNamingPolicy. All these generated classes are named after "EnhancedByCloudStack" pattern, you may notice it in log messages or in debug session.

...

4.6 Pluggable adapters

Adapter components usually works under the management of its manager component, a same set of adapter components may be used by multiple managers, and sometimes, order of the adapters may also be significant. Whether or not an adapter component is in action depends not only the existence of its <bean> declaration, but also the references in manager components.

...