Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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

	@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 CloudStacklimit 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)

...

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(actionEvent.create()){
                //This 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 for "+eventDescription);
                ctx.setStartEventId(startEventId);
            } else {
                EventUtils.saveEvent(userId, accountId, EventVO.LEVEL_INFO, actionEvent.eventType(), "Successfully completed "+eventDescription, startEventId);
            }
        }
    }

    @Override
    public void interceptException(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(actionEvent.create()){
                long eventId = EventUtils.saveCreatedEvent(userId, accountId, EventVO.LEVEL_ERROR, actionEvent.eventType(), "Error while creating entity for "+eventDescription);
                ctx.setStartEventId(eventId);
            } else {
                EventUtils.saveEvent(userId, accountId, EventVO.LEVEL_ERROR, actionEvent.eventType(), "Error while "+eventDescription, startEventId);
            }
        }
    }

    @Override
    public boolean needToIntercept(Method method) {
        ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
        if (actionEvent != null) {
            return true;
        }

        return false;
    }
}

If you ever need to implement a method interceptor, implement interface ComponentMethodInterceptor and declare it 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. in 

Code Block

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

  <bean id="instantiatePostProcessor" class="com.cloud.utils.component.ComponentInstantiationPostProcessor">
    <property name="Interceptors">
        <list>
            <ref bean="transactionContextBuilder" />
            <ref bean="actionEventInterceptor" />
        </list>
    </property>
  </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 for above AOP pattern. 

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.

...