DUE TO SPAM, SIGN-UP IS DISABLED. Goto Selfserve wiki signup and request an account.

DUE TO SPAM, SIGN-UP IS DISABLED. Goto Selfserve wiki signup and request an account.
...
| Code Block |
|---|
if (component is mandatory to OSS/non-OSS ) {
if( component shares common configuration in both OSS/ and non-OSS distributions ) {
put it in applicationContext.xml.in
} else {
put it in both componentContext.xml and nonossComponentContext.xml.in
}
} else {
if(component is for OSS only)
put it in componentContext.xml
else
put it in nonossComponentContext.xml
} |
...
Spring component is nothing else more than a Java POJO object, once you've determined which file the component declaration should go using the flow at above. declaring it is fairly simple. Following gives an example
...
ID of the component should be unique, as original configuration file files are generated automatically from legacy code, it is usually named after the component class name.
Avoid using @Component annotation to declare your component, it is not JSR compliance, and we no longer support component auto-scanning to load up components annotated by @Component
One of the biggest advantage of switching to Spring is that Auto-wiring in Component components has now become consistent everywhere. With previous legacy ComponentLocator, there are a lot of places that have to use run-time wiring, for example, using ComponentLocator.getManager() to wire a reference to a manager component. The reason for developers to do so is that ComponentLocator does not fully resolved resolve dependent-injection for components, so when inter-component relationship becomes complex in a large system like CloudStack, lots of hacking ways rise up inside CloudStack codebase. Although it solves the immediate needs, but it also creates a lot of confusion for developers, the more components being involved into injection business, the more the system is tightly coupled with ComponentLocator itself.
Following is an example of such hacking way, inside BaseCmd class, developer tried to resolve all the references for every used components into static variables at BaseCmd. It also basically means that, when you add a new Command class to the system, you will have to remember to do something about your new service at BaseCmd class
| 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
}
|
With Spring, developer can now always use @Inject annotation to declare such reference. One , and most importantly, inject these references only when they are needed, so that each Command class can become more independent to each other. This is one of the top two reasons for us to try Spring experiments in Javelin, since with more complete dependency injection, it gives it can give us a cleaner component coding practice. (the other top reason is that we have broader integration supports support from many other third-party vendors, i.e., JUnit)).
Although we've switched whole code base to Spring, there are still a lot of legacy coding-practices like above that need to cleanup. Once you've seen one of these, feel free to help cleanup them.
With previous legacy ComponentLocator, we have many places that use run-time resolution for fields that can't be resolved automatically by ComponentLocator's injection, and in many of these cases, these run-time resolutions happen at object construction time. Under Spring, such way won't work any more. The reason is that when Spring constructs the component object, the added runtime logic running inside the constructor can not feed information back into Spring injection framework, it may cause the process to be brokenbreak. The solution to this issues is to always leave the object construction and auto-wiring work to Spring, and leave the component initialization later later.
Protected or private constructors may be a good coding practice to enforce certain usage pattern of the class. However, protected or private constructors are not friendly to Spring injection process. Please always use public constructors, for the same reason, if you POJO class wants to be Spring component, don't seal the class by putting final modifier to your class
...
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 now switched to use standard javax @Inject auto-wiring annotationmechanism, it is even possible for your component to run in containers that are other than CloudStack.
We 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(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 in applicationContext.xml.in.
| Code Block |
|---|
<bean id="instantiatePostProcessor" class="com.cloud.utils.component.ComponentInstantiationPostProcessor">
<property name="Interceptors">
<list>
<ref bean="transactionContextBuilder" />
<ref bean="actionEventInterceptor" />
</list>
</property>
</bean>
|
In CloudStack, some components are lifecyle sensitive, examples are those manager objects, adapter objects, to make lifecyle management more general and flexible, there are some TODO works to make component life-cycle management easier. We'll keep this topic updated in the community.