package act.job.bytecode;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.
* #L%
*/
import act.app.App;
import act.inject.param.ParamValueLoaderManager;
import act.inject.param.ParamValueLoaderService;
import act.job.JobContext;
import act.job.meta.JobClassMetaInfo;
import act.job.meta.JobMethodMetaInfo;
import act.sys.Env;
import com.esotericsoftware.reflectasm.MethodAccess;
import org.osgl.$;
import org.osgl.exception.NotAppliedException;
import org.osgl.inject.BeanSpec;
import org.osgl.util.E;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
/**
* Implement handler using
* https://github.com/EsotericSoftware/reflectasm
*/
public class ReflectedJobInvoker<M extends JobMethodMetaInfo> extends $.F0<Object> {
private App app;
private ClassLoader cl;
private JobClassMetaInfo classInfo;
private Class<?> jobClass;
private MethodAccess methodAccess;
private M methodInfo;
private int methodIndex;
protected Method method;
private List<BeanSpec> providedParams;
private boolean disabled;
private ParamValueLoaderService paramValueLoaderService;
public ReflectedJobInvoker(M handlerMetaInfo, App app) {
this.cl = app.classLoader();
this.methodInfo = handlerMetaInfo;
this.classInfo = handlerMetaInfo.classInfo();
this.app = app;
}
private void init() {
disabled = false;
jobClass = $.classForName(classInfo.className(), cl);
disabled = disabled || !Env.matches(jobClass);
method = methodInfo.method();
disabled = disabled || !Env.matches(jobClass);
providedParams = methodInfo.paramTypes();
ParamValueLoaderManager paramValueLoaderManager = app.service(ParamValueLoaderManager.class);
if (null != paramValueLoaderManager) {
paramValueLoaderService = paramValueLoaderManager.get(JobContext.class);
} else {
// this job is scheduled to run before ParamValueLoaderManager initialized
}
if (!Modifier.isStatic(method.getModifiers())) {
Class[] paramTypes = paramTypes();
//constructorAccess = ConstructorAccess.get(controllerClass);
methodAccess = MethodAccess.get(jobClass);
methodIndex = methodAccess.getIndex(methodInfo.name(), paramTypes);
} else {
method.setAccessible(true);
}
}
@Override
public Object apply() throws NotAppliedException, $.Break {
if (null == jobClass) {
init();
}
if (disabled) {
return null;
}
Object job = jobClassInstance(app);
return invoke(job);
}
private Class[] paramTypes() {
List<BeanSpec> paramTypes = methodInfo.paramTypes();
int sz = null == paramTypes ? 0 : paramTypes.size();
Class[] ca = new Class[sz];
for (int i = 0; i < sz; ++i) {
BeanSpec spec = methodInfo.paramTypes().get(i);
ca[i] = spec.rawType();
}
return ca;
}
private Object jobClassInstance(App app) {
return null != paramValueLoaderService ? paramValueLoaderService.loadHostBean(jobClass, JobContext.current())
: app.getInstance(jobClass);
}
private Object invoke(Object jobClassInstance) {
Object[] params = params(jobClassInstance);
Object result;
if (null != methodAccess) {
result = methodAccess.invoke(jobClassInstance, methodIndex, params);
} else {
try {
result = method.invoke(jobClassInstance, params);
} catch (InvocationTargetException e) {
throw E.unexpected(e.getCause());
} catch (Exception e) {
throw E.unexpected(e);
}
}
return result;
}
private Object[] params(Object job) {
if (null != paramValueLoaderService) {
return paramValueLoaderService.loadMethodParams(job, method, JobContext.current());
}
E.illegalStateIf(paramTypes().length > 0, "Cannot invoke job with parameters before app fully started");
return new Object[0];
}
}