mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 20:02:29 +01:00
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:
parent
95011d6bf3
commit
3ab744d100
@ -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
|
||||
-->
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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() + "!");
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user