CLOUDSTACK-1795: implement custom AOP to fully support legacy CloudStack AOP semantcis

Signed-off-by: Chip Childers <chip.childers@gmail.com>
This commit is contained in:
Kelven Yang 2013-03-27 13:06:49 +00:00
parent 95011d6bf3
commit 3ab744d100
20 changed files with 383 additions and 196 deletions

View File

@ -35,27 +35,18 @@
<!--
@DB support
-->
<aop:config>
<aop:aspect id="dbContextBuilder" ref="transactionContextBuilder">
<aop:pointcut id="captureAnyMethod"
expression="execution(* *(..))"
/>
<aop:around pointcut-ref="captureAnyMethod" method="AroundAnyMethod"/>
</aop:aspect>
<aop:aspect id="actionEventInterceptorAspect" ref="actionEventInterceptor">
<aop:pointcut id="captureEventMethod"
expression="execution(* *(..)) and @annotation(com.cloud.event.ActionEvent)"
/>
<aop:around pointcut-ref="captureEventMethod" method="AroundAnyMethod"/>
</aop:aspect>
</aop:config>
<bean id="transactionContextBuilder" class="com.cloud.utils.db.TransactionContextBuilder" />
<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>
<!--
RPC/Async/EventBus
-->

View File

@ -138,8 +138,7 @@ public class ApiDispatcher {
UserContext ctx = UserContext.current();
ctx.setAccountId(cmd.getEntityOwnerId());
BaseCmd realCmdObj = ComponentContext.getTargetObject(cmd);
if (realCmdObj instanceof BaseAsyncCmd) {
if (cmd instanceof BaseAsyncCmd) {
BaseAsyncCmd asyncCmd = (BaseAsyncCmd) cmd;
String startEventId = params.get("ctxStartEventId");
@ -171,8 +170,6 @@ public class ApiDispatcher {
Map<Object, AccessType> entitiesToAccess = new HashMap<Object, AccessType>();
Map<String, Object> unpackedParams = cmd.unpackParams(params);
cmd = ComponentContext.getTargetObject(cmd);
if (cmd instanceof BaseListCmd) {
Object pageSizeObj = unpackedParams.get(ApiConstants.PAGE_SIZE);
Long pageSize = null;

View File

@ -388,16 +388,15 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
Long callerUserId = ctx.getCallerUserId();
Account caller = ctx.getCaller();
BaseCmd realCmdObj = ComponentContext.getTargetObject(cmdObj);
// Queue command based on Cmd super class:
// BaseCmd: cmd is dispatched to ApiDispatcher, executed, serialized and returned.
// BaseAsyncCreateCmd: cmd params are processed and create() is called, then same workflow as BaseAsyncCmd.
// BaseAsyncCmd: cmd is processed and submitted as an AsyncJob, job related info is serialized and returned.
if (realCmdObj instanceof BaseAsyncCmd) {
if (cmdObj instanceof BaseAsyncCmd) {
Long objectId = null;
String objectUuid = null;
if (realCmdObj instanceof BaseAsyncCreateCmd) {
if (cmdObj instanceof BaseAsyncCreateCmd) {
BaseAsyncCreateCmd createCmd = (BaseAsyncCreateCmd) cmdObj;
_dispatcher.dispatchCreateCmd(createCmd, params);
objectId = createCmd.getEntityId();
@ -433,7 +432,7 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
ctx.setAccountId(asyncCmd.getEntityOwnerId());
Long instanceId = (objectId == null) ? asyncCmd.getInstanceId() : objectId;
AsyncJobVO job = new AsyncJobVO(callerUserId, caller.getId(), realCmdObj.getClass().getName(),
AsyncJobVO job = new AsyncJobVO(callerUserId, caller.getId(), cmdObj.getClass().getName(),
ApiGsonHelper.getBuilder().create().toJson(params), instanceId, asyncCmd.getInstanceType());
long jobId = _asyncMgr.submitAsyncJob(job);
@ -457,22 +456,22 @@ public class ApiServer implements HttpRequestHandler, ApiServerService {
// if the command is of the listXXXCommand, we will need to also return the
// the job id and status if possible
// For those listXXXCommand which we have already created DB views, this step is not needed since async job is joined in their db views.
if (realCmdObj instanceof BaseListCmd && !(realCmdObj instanceof ListVMsCmd) && !(realCmdObj instanceof ListRoutersCmd)
&& !(realCmdObj instanceof ListSecurityGroupsCmd)
&& !(realCmdObj instanceof ListTagsCmd)
&& !(realCmdObj instanceof ListEventsCmd)
&& !(realCmdObj instanceof ListVMGroupsCmd)
&& !(realCmdObj instanceof ListProjectsCmd)
&& !(realCmdObj instanceof ListProjectAccountsCmd)
&& !(realCmdObj instanceof ListProjectInvitationsCmd)
&& !(realCmdObj instanceof ListHostsCmd)
&& !(realCmdObj instanceof ListVolumesCmd)
&& !(realCmdObj instanceof ListUsersCmd)
&& !(realCmdObj instanceof ListAccountsCmd)
&& !(realCmdObj instanceof ListStoragePoolsCmd)
&& !(realCmdObj instanceof ListDiskOfferingsCmd)
&& !(realCmdObj instanceof ListServiceOfferingsCmd)
&& !(realCmdObj instanceof ListZonesByCmd)
if (cmdObj instanceof BaseListCmd && !(cmdObj instanceof ListVMsCmd) && !(cmdObj instanceof ListRoutersCmd)
&& !(cmdObj instanceof ListSecurityGroupsCmd)
&& !(cmdObj instanceof ListTagsCmd)
&& !(cmdObj instanceof ListEventsCmd)
&& !(cmdObj instanceof ListVMGroupsCmd)
&& !(cmdObj instanceof ListProjectsCmd)
&& !(cmdObj instanceof ListProjectAccountsCmd)
&& !(cmdObj instanceof ListProjectInvitationsCmd)
&& !(cmdObj instanceof ListHostsCmd)
&& !(cmdObj instanceof ListVolumesCmd)
&& !(cmdObj instanceof ListUsersCmd)
&& !(cmdObj instanceof ListAccountsCmd)
&& !(cmdObj instanceof ListStoragePoolsCmd)
&& !(cmdObj instanceof ListDiskOfferingsCmd)
&& !(cmdObj instanceof ListServiceOfferingsCmd)
&& !(cmdObj instanceof ListZonesByCmd)
) {
buildAsyncListResponse((BaseListCmd) cmdObj, caller);
}

View File

@ -365,11 +365,11 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager {
try {
// schedule a scan task immediately
if (ComponentContext.getTargetObject(_agentMgr) instanceof ClusteredAgentManagerImpl) {
if (_agentMgr instanceof ClusteredAgentManagerImpl) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Received notification as part of addHost command to start a host scan task");
}
ClusteredAgentManagerImpl clusteredAgentMgr = (ClusteredAgentManagerImpl)ComponentContext.getTargetObject(_agentMgr);
ClusteredAgentManagerImpl clusteredAgentMgr = (ClusteredAgentManagerImpl)_agentMgr;
clusteredAgentMgr.scheduleHostScanTask();
}
} catch (Exception e) {

View File

@ -16,55 +16,22 @@
// under the License.
package com.cloud.event;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import com.cloud.user.UserContext;
import com.cloud.utils.component.ComponentMethodProxyCache;
import com.cloud.utils.component.ComponentMethodInterceptor;
public class ActionEventInterceptor {
public class ActionEventInterceptor implements ComponentMethodInterceptor {
private static final Logger s_logger = Logger.getLogger(ActionEventInterceptor.class);
public ActionEventInterceptor() {
}
public Object AroundAnyMethod(ProceedingJoinPoint call) throws Throwable {
MethodSignature methodSignature = (MethodSignature)call.getSignature();
// Note: AOP for ActionEvent is triggered annotation, no need to check the annotation on method again
Method targetMethod = ComponentMethodProxyCache.getTargetMethod(
methodSignature.getMethod(), call.getTarget());
if(targetMethod != null) {
EventVO event = interceptStart(targetMethod);
boolean success = true;
Object ret = null;
try {
ret = call.proceed();
} catch (Throwable e) {
success = false;
interceptException(targetMethod, event);
throw e;
} finally {
if(success){
interceptComplete(targetMethod, event);
}
}
return ret;
} else {
s_logger.error("Unable to find the proxied method behind. Method: " + methodSignature.getMethod().getName());
}
return call.proceed();
}
public EventVO interceptStart(AnnotatedElement element) {
@Override
public Object interceptStart(Method method, Object target) {
EventVO event = null;
Method method = (Method)element;
ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
if (actionEvent != null) {
boolean async = actionEvent.async();
@ -83,8 +50,8 @@ public class ActionEventInterceptor {
return event;
}
public void interceptComplete(AnnotatedElement element, EventVO event) {
Method method = (Method)element;
@Override
public void interceptComplete(Method method, Object target, Object event) {
ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
if (actionEvent != null) {
UserContext ctx = UserContext.current();
@ -105,8 +72,8 @@ public class ActionEventInterceptor {
}
}
public void interceptException(AnnotatedElement element, EventVO event) {
Method method = (Method)element;
@Override
public void interceptException(Method method, Object target, Object event) {
ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
if (actionEvent != null) {
UserContext ctx = UserContext.current();
@ -126,7 +93,8 @@ public class ActionEventInterceptor {
}
}
private boolean needToIntercept(Method method) {
@Override
public boolean needToIntercept(Method method) {
ActionEvent actionEvent = method.getAnnotation(ActionEvent.class);
if (actionEvent != null) {
return true;

View File

@ -1113,7 +1113,7 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase
}
NetworkElement element = _networkModel.getElementImplementingProvider(providers.get(0).getName());
if (!(ComponentContext.getTargetObject(element) instanceof IpDeployer)) {
if (!(element instanceof IpDeployer)) {
s_logger.error("The firewall provider for network " + network.getName() + " don't have ability to deploy IP address!");
return null;
}

View File

@ -611,10 +611,10 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L
}
IpDeployer deployer = null;
NetworkElement element = _networkModel.getElementImplementingProvider(provider.getName());
if (!(ComponentContext.getTargetObject(element) instanceof IpDeployingRequester)) {
if (!(element instanceof IpDeployingRequester)) {
throw new CloudRuntimeException("Element " + element + " is not a IpDeployingRequester!");
}
deployer = ((IpDeployingRequester)ComponentContext.getTargetObject(element)).getIpDeployer(network);
deployer = ((IpDeployingRequester)element).getIpDeployer(network);
if (deployer == null) {
throw new CloudRuntimeException("Fail to get ip deployer for element: " + element);
}
@ -1594,13 +1594,13 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L
if (vmProfile.getType() == Type.User && element.getProvider() != null) {
if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp) &&
_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, element.getProvider()) &&
(ComponentContext.getTargetObject(element) instanceof DhcpServiceProvider)) {
element instanceof DhcpServiceProvider) {
DhcpServiceProvider sp = (DhcpServiceProvider) element;
sp.addDhcpEntry(network, profile, vmProfile, dest, context);
}
if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.UserData) &&
_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.UserData, element.getProvider()) &&
(ComponentContext.getTargetObject(element) instanceof UserDataServiceProvider)) {
element instanceof UserDataServiceProvider) {
UserDataServiceProvider sp = (UserDataServiceProvider) element;
sp.addPasswordAndUserdata(network, profile, vmProfile, dest, context);
}
@ -3678,15 +3678,15 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L
@Override
public StaticNatServiceProvider getStaticNatProviderForNetwork(Network network) {
NetworkElement element = getElementForServiceInNetwork(network, Service.StaticNat);
assert ComponentContext.getTargetObject(element) instanceof StaticNatServiceProvider;
return (StaticNatServiceProvider)ComponentContext.getTargetObject(element);
assert element instanceof StaticNatServiceProvider;
return (StaticNatServiceProvider)element;
}
@Override
public LoadBalancingServiceProvider getLoadBalancingProviderForNetwork(Network network) {
NetworkElement element = getElementForServiceInNetwork(network, Service.Lb);
assert ComponentContext.getTargetObject(element) instanceof LoadBalancingServiceProvider;
return ( LoadBalancingServiceProvider)ComponentContext.getTargetObject(element);
assert element instanceof LoadBalancingServiceProvider;
return (LoadBalancingServiceProvider)element;
}
@Override
public boolean isNetworkInlineMode(Network network) {

View File

@ -404,9 +404,9 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel {
Network network = _networksDao.findById(networkId);
NetworkElement oldElement = getElementImplementingProvider(oldProvider.getName());
NetworkElement newElement = getElementImplementingProvider(newProvider.getName());
if (ComponentContext.getTargetObject(oldElement) instanceof IpDeployingRequester && ComponentContext.getTargetObject(newElement) instanceof IpDeployingRequester) {
IpDeployer oldIpDeployer = ((IpDeployingRequester)ComponentContext.getTargetObject(oldElement)).getIpDeployer(network);
IpDeployer newIpDeployer = ((IpDeployingRequester)ComponentContext.getTargetObject(newElement)).getIpDeployer(network);
if (oldElement instanceof IpDeployingRequester && newElement instanceof IpDeployingRequester) {
IpDeployer oldIpDeployer = ((IpDeployingRequester)oldElement).getIpDeployer(network);
IpDeployer newIpDeployer = ((IpDeployingRequester)newElement).getIpDeployer(network);
if (!oldIpDeployer.getProvider().getName().equals(newIpDeployer.getProvider().getName())) {
throw new InvalidParameterException("There would be multiple providers for IP " + publicIp.getAddress() + "!");
}

View File

@ -65,6 +65,7 @@ import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.secondary.SecondaryStorageVmManager;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ComponentMethodInterceptable;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.vm.UserVmManager;
@ -77,7 +78,7 @@ import com.cloud.vm.dao.UserVmDao;
*
*/
@Component
public class StatsCollector {
public class StatsCollector implements ComponentMethodInterceptable {
public static final Logger s_logger = Logger.getLogger(StatsCollector.class.getName());
private static StatsCollector s_instance = null;

View File

@ -19,7 +19,7 @@ package com.cloud.utils.component;
import java.util.List;
// Typical Adapter implementation.
public class AdapterBase extends ComponentLifecycleBase implements Adapter {
public class AdapterBase extends ComponentLifecycleBase implements Adapter, ComponentMethodInterceptable {
public AdapterBase() {
// set default run level for adapter components
@ -29,7 +29,7 @@ public class AdapterBase extends ComponentLifecycleBase implements Adapter {
public static <T extends Adapter> T getAdapterByName(List<T> adapters, String name) {
for(T adapter : adapters) {
if(adapter.getName() != null && adapter.getName().equalsIgnoreCase(name))
return ComponentContext.getTargetObject(adapter);
return adapter;
}
return null;
}

View File

@ -59,16 +59,18 @@ public class ComponentContext implements ApplicationContextAware {
public static ApplicationContext getApplicationContext() {
return s_appContext;
}
}
public static void initComponentsLifeCycle() {
// Run the SystemIntegrityCheckers first
Map<String, SystemIntegrityChecker> integrityCheckers = getApplicationContext().getBeansOfType(SystemIntegrityChecker.class);
for (Entry<String,SystemIntegrityChecker> entry : integrityCheckers.entrySet() ){
s_logger.info ("Running SystemIntegrityChecker " + entry.getKey());
entry.getValue().check();
}
AutowireCapableBeanFactory beanFactory = s_appContext.getAutowireCapableBeanFactory();
Map<String, ComponentMethodInterceptable> interceptableComponents = getApplicationContext().getBeansOfType(
ComponentMethodInterceptable.class);
for(Map.Entry<String, ComponentMethodInterceptable> entry : interceptableComponents.entrySet()) {
Object bean = getTargetObject(entry.getValue());
beanFactory.configureBean(bean, entry.getKey());
}
Map<String, ComponentLifecycle> lifecyleComponents = getApplicationContext().getBeansOfType(ComponentLifecycle.class);
Map[] classifiedComponents = new Map[ComponentLifecycle.MAX_RUN_LEVELS];
@ -103,6 +105,18 @@ public class ComponentContext implements ApplicationContextAware {
avoidMap.put(implClassName, implClassName);
}
}
// Run the SystemIntegrityCheckers first
Map<String, SystemIntegrityChecker> integrityCheckers = getApplicationContext().getBeansOfType(SystemIntegrityChecker.class);
for (Entry<String,SystemIntegrityChecker> entry : integrityCheckers.entrySet() ){
s_logger.info ("Running SystemIntegrityChecker " + entry.getKey());
try {
entry.getValue().check();
} catch(Throwable e) {
s_logger.error("System integrity check failed. Refuse to startup");
System.exit(1);
}
}
// starting phase
avoidMap.clear();

View File

@ -0,0 +1,152 @@
// 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
// 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.utils.component;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;
import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.cloud.utils.Pair;
public class ComponentInstantiationPostProcessor implements InstantiationAwareBeanPostProcessor {
private static final Logger s_logger = Logger.getLogger(ComponentInstantiationPostProcessor.class);
private List<ComponentMethodInterceptor> _interceptors = new ArrayList<ComponentMethodInterceptor>();
private Callback[] _callbacks;
private CallbackFilter _callbackFilter;
public ComponentInstantiationPostProcessor() {
_callbacks = new Callback[2];
_callbacks[0] = NoOp.INSTANCE;
_callbacks[1] = new InterceptorDispatcher();
_callbackFilter = new InterceptorFilter();
}
public List<ComponentMethodInterceptor> getInterceptors() {
return _interceptors;
}
public void setInterceptors(List<ComponentMethodInterceptor> interceptors) {
_interceptors = interceptors;
}
private Callback[] getCallbacks() {
return _callbacks;
}
private CallbackFilter getCallbackFilter() {
return _callbackFilter;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass,
String beanName) throws BeansException {
if(_interceptors != null && _interceptors.size() > 0) {
if(ComponentMethodInterceptable.class.isAssignableFrom(beanClass)) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanClass);
enhancer.setCallbacks(getCallbacks());
enhancer.setCallbackFilter(getCallbackFilter());
enhancer.setNamingPolicy(ComponentNamingPolicy.INSTANCE);
Object bean = enhancer.create();
return bean;
}
}
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName)
throws BeansException {
return true;
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException {
return pvs;
}
protected class InterceptorDispatcher implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
ArrayList<Pair<ComponentMethodInterceptor, Object>> interceptors = new ArrayList<Pair<ComponentMethodInterceptor, Object>>();
for (ComponentMethodInterceptor interceptor : getInterceptors()) {
if (interceptor.needToIntercept(method)) {
Object objReturnedInInterceptStart = interceptor.interceptStart(method, target);
interceptors.add(new Pair<ComponentMethodInterceptor, Object>(interceptor, objReturnedInInterceptStart));
}
}
boolean success = false;
try {
Object obj = methodProxy.invokeSuper(target, args);
success = true;
return obj;
} finally {
for (Pair<ComponentMethodInterceptor, Object> interceptor : interceptors) {
if (success) {
interceptor.first().interceptComplete(method, target, interceptor.second());
} else {
interceptor.first().interceptException(method, target, interceptor.second());
}
}
}
}
}
protected class InterceptorFilter implements CallbackFilter {
@Override
public int accept(Method method) {
for(ComponentMethodInterceptor interceptor : getInterceptors()) {
if (interceptor.needToIntercept(method)) {
return 1;
}
}
return 0;
}
}
}

View File

@ -0,0 +1,24 @@
// 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
// 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.utils.component;
/**
* Marker interface to work with CGLIB based CloudStack legacy AOP
*
*/
public interface ComponentMethodInterceptable {
}

View File

@ -0,0 +1,27 @@
// 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
// 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.utils.component;
import java.lang.reflect.Method;
public interface ComponentMethodInterceptor {
boolean needToIntercept(Method method);
Object interceptStart(Method method, Object target);
void interceptComplete(Method method, Object target, Object objReturnedInInterceptStart);
void interceptException(Method method, Object target, Object objReturnedInInterceptStart);
}

View File

@ -0,0 +1,63 @@
// 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
// 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.utils.component;
import net.sf.cglib.core.NamingPolicy;
import net.sf.cglib.core.Predicate;
/**
* Copied/Modified from Spring source
*
*/
public class ComponentNamingPolicy implements NamingPolicy {
public static final ComponentNamingPolicy INSTANCE = new ComponentNamingPolicy();
public String getClassName(String prefix, String source, Object key, Predicate names) {
if (prefix == null) {
prefix = "net.sf.cglib.empty.Object";
} else if (prefix.startsWith("java")) {
prefix = "_" + prefix;
}
String base =
prefix + "_" +
source.substring(source.lastIndexOf('.') + 1) +
getTag() + "_" +
Integer.toHexString(key.hashCode());
String attempt = base;
int index = 2;
while (names.evaluate(attempt))
attempt = base + "_" + index++;
return attempt;
}
/**
* Returns a string which is incorporated into every generated class name.
* By default returns "ByCloudStack"
*/
protected String getTag() {
return "ByCloudStack";
}
public int hashCode() {
return getTag().hashCode();
}
public boolean equals(Object o) {
return (o instanceof ComponentNamingPolicy) && ((ComponentNamingPolicy) o).getTag().equals(getTag());
}
}

View File

@ -16,7 +16,7 @@
// under the License.
package com.cloud.utils.component;
public class ManagerBase extends ComponentLifecycleBase {
public class ManagerBase extends ComponentLifecycleBase implements ComponentMethodInterceptable {
public ManagerBase() {
// set default run level for manager components
setRunLevel(ComponentLifecycle.RUN_LEVEL_COMPONENT_BOOTSTRAP);

View File

@ -71,6 +71,7 @@ import com.cloud.utils.Ternary;
import com.cloud.utils.component.ComponentContext;
import com.cloud.utils.component.ComponentLifecycle;
import com.cloud.utils.component.ComponentLifecycleBase;
import com.cloud.utils.component.ComponentMethodInterceptable;
import com.cloud.utils.crypt.DBEncryptionUtil;
import com.cloud.utils.db.SearchCriteria.SelectType;
import com.cloud.utils.exception.CloudRuntimeException;
@ -114,7 +115,7 @@ import edu.emory.mathcs.backport.java.util.Collections;
*
**/
@DB
public abstract class GenericDaoBase<T, ID extends Serializable> extends ComponentLifecycleBase implements GenericDao<T, ID> {
public abstract class GenericDaoBase<T, ID extends Serializable> extends ComponentLifecycleBase implements GenericDao<T, ID>, ComponentMethodInterceptable {
private final static Logger s_logger = Logger.getLogger(GenericDaoBase.class);
protected final static TimeZone s_gmtTimeZone = TimeZone.getTimeZone("GMT");
@ -193,15 +194,14 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
( (Class<?>)((Class<?>)t).getGenericSuperclass()).getGenericSuperclass()).getActualTypeArguments()[0];
}
/*
s_daoMaps.put(_entityBeanType, ComponentContext.getComponent(this.getClass()));
s_daoMaps.put(_entityBeanType, this);
Class<?>[] interphaces = _entityBeanType.getInterfaces();
if (interphaces != null) {
for (Class<?> interphace : interphaces) {
s_daoMaps.put(interphace, ComponentContext.getComponent(this.getClass()));
s_daoMaps.put(interphace, this);
}
}
*/
_table = DbUtil.getTableName(_entityBeanType);
final SqlGenerator generator = new SqlGenerator(_entityBeanType);
@ -1750,25 +1750,6 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
_name = name;
Class<?> daoInterface = null;
for(Class<?> intf : this.getClass().getInterfaces()) {
if(GenericDao.class.isAssignableFrom(intf)) {
daoInterface = intf;
break;
}
}
if(daoInterface != null) {
s_logger.info("Register dao interface in GenericDaoBase entity-DAO map. " + daoInterface.getName());
s_daoMaps.put(_entityBeanType, (GenericDao<?, ? extends Serializable>) ComponentContext.getComponent(daoInterface));
Class<?>[] interphaces = _entityBeanType.getInterfaces();
if (interphaces != null) {
for (Class<?> interphace : interphaces) {
s_daoMaps.put(interphace, (GenericDao<?, ? extends Serializable>) ComponentContext.getComponent(daoInterface));
}
}
}
final String value = (String)params.get("lock.timeout");
_timeoutSeconds = NumbersUtil.parseInt(value, 300);

View File

@ -18,65 +18,20 @@ package com.cloud.utils.db;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import com.cloud.utils.component.ComponentMethodInterceptor;
import com.cloud.utils.component.ComponentMethodProxyCache;
public class TransactionContextBuilder implements MethodInterceptor {
private static final Logger s_logger = Logger.getLogger(TransactionContextBuilder.class);
public class TransactionContextBuilder implements ComponentMethodInterceptor {
public TransactionContextBuilder() {
}
public Object AroundAnyMethod(ProceedingJoinPoint call) throws Throwable {
MethodSignature methodSignature = (MethodSignature)call.getSignature();
Method targetMethod = methodSignature.getMethod();
if(needToIntercept(targetMethod, call.getTarget())) {
Transaction txn = Transaction.open(call.getSignature().getName());
Object ret = null;
try {
ret = call.proceed();
} finally {
txn.close();
}
return ret;
}
return call.proceed();
}
@Override
public Object invoke(MethodInvocation method) throws Throwable {
Method targetMethod = method.getMethod();
if(needToIntercept(targetMethod, method.getThis())) {
Transaction txn = Transaction.open(targetMethod.getName());
Object ret = null;
try {
ret = method.proceed();
} finally {
txn.close();
}
return ret;
}
return method.proceed();
}
private boolean needToIntercept(Method method, Object target) {
public boolean needToIntercept(Method method) {
DB db = method.getAnnotation(DB.class);
if (db != null) {
return true;
}
Class<?> clazz = method.getDeclaringClass();
if(clazz.isInterface()) {
clazz = target.getClass();
Method targetMethod = ComponentMethodProxyCache.getTargetMethod(method, target);
if(targetMethod != null && targetMethod.getAnnotation(DB.class) != null)
return true;
}
do {
db = clazz.getAnnotation(DB.class);
@ -88,4 +43,23 @@ public class TransactionContextBuilder implements MethodInterceptor {
return false;
}
@Override
public Object interceptStart(Method method, Object target) {
return Transaction.open(method.getName());
}
@Override
public void interceptComplete(Method method, Object target, Object objReturnedInInterceptStart) {
Transaction txn = (Transaction)objReturnedInInterceptStart;
if(txn != null)
txn.close();
}
@Override
public void interceptException(Method method, Object target, Object objReturnedInInterceptStart) {
Transaction txn = (Transaction)objReturnedInInterceptStart;
if(txn != null)
txn.close();
}
}

View File

@ -80,7 +80,7 @@ public class CglibThrowableRendererTest extends TestCase {
Writer w = new CharArrayWriter();
Logger alt = getAlternateLogger(w, null);
TestClass test = ComponentContext.inject(TestClass.class);
TestClass test = new TestClass();
try {
test.exception();
} catch (Exception e) {

View File

@ -33,22 +33,18 @@
<context:annotation-config />
<context:component-scan base-package="org.apache.cloudstack, com.cloud" />
<!--
@DB support
-->
<aop:config proxy-target-class="true">
<aop:aspect id="dbContextBuilder" ref="transactionContextBuilder">
<aop:pointcut id="captureAnyMethod"
expression="execution(* *(..))"
/>
<aop:around pointcut-ref="captureAnyMethod" method="AroundAnyMethod"/>
</aop:aspect>
</aop:config>
<bean id="transactionContextBuilder" class="com.cloud.utils.db.TransactionContextBuilder" />
<bean id="instantiatePostProcessor" class="com.cloud.utils.component.ComponentInstantiationPostProcessor">
<property name="Interceptors">
<list>
<ref bean="transactionContextBuilder" />
</list>
</property>
</bean>
</beans>