package act.cli.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.cli.CliContext; import act.cli.CommandExecutor; import act.cli.meta.CommandMethodMetaInfo; import act.cli.meta.CommandParamMetaInfo; import act.inject.param.CliContextParamLoader; import act.inject.param.ParamValueLoaderManager; import com.esotericsoftware.reflectasm.MethodAccess; import org.osgl.$; import org.osgl.util.E; import java.lang.reflect.Method; /** * Implement {@link act.cli.CommandExecutor} using * https://github.com/EsotericSoftware/reflectasm */ public class ReflectedCommandExecutor extends CommandExecutor { private static final Object[] DUMP_PARAMS = new Object[0]; private CommandMethodMetaInfo methodMetaInfo; private App app; private CliContextParamLoader paramLoaderService; private ClassLoader cl; private Class[] paramTypes; private Class<?> commanderClass; private Method method; private MethodAccess methodAccess; private int commandIndex; private int paramCount; private CliContext.ParsingContext parsingContext; public ReflectedCommandExecutor(CommandMethodMetaInfo methodMetaInfo, App app) { this.methodMetaInfo = $.notNull(methodMetaInfo); this.app = $.NPE(app); this.cl = app.classLoader(); this.paramTypes = paramTypes(); this.paramCount = methodMetaInfo.paramCount(); this.commanderClass = $.classForName(methodMetaInfo.classInfo().className(), cl); try { this.method = commanderClass.getMethod(methodMetaInfo.methodName(), paramTypes); } catch (NoSuchMethodException e) { throw E.unexpected(e); } if (!methodMetaInfo.isStatic()) { methodAccess = MethodAccess.get(commanderClass); commandIndex = methodAccess.getIndex(methodMetaInfo.methodName(), paramTypes); } else { method.setAccessible(true); } this.paramLoaderService = app.service(ParamValueLoaderManager.class).get(CliContext.class); this.buildParsingContext(); } @Override public Object execute(CliContext context) { context.attribute(CliContext.ATTR_METHOD, method); context.prepare(parsingContext); paramLoaderService.preParseOptions(method, methodMetaInfo, context); Object cmd = commanderInstance(context); Object[] params = params(cmd, context); return invoke(cmd, params); } @Override protected void releaseResources() { app = null; cl = null; commandIndex = 0; commanderClass = null; method = null; methodAccess = null; paramTypes = null; super.releaseResources(); } private Object commanderInstance(CliContext context) { String commander = commanderClass.getName(); Object inst = context.__commanderInstance(commander); if (null == inst) { inst = paramLoaderService.loadHostBean(commanderClass, context); context.__commanderInstance(commander, inst); } return inst; } private Class<?>[] paramTypes() { int paramCount = methodMetaInfo.paramCount(); Class<?>[] ca = new Class[paramCount]; if (0 == paramCount) { return ca; } for (int i = 0; i < paramCount; ++i) { CommandParamMetaInfo param = methodMetaInfo.param(i); String className = param.type().getClassName(); ca[i] = $.classForName(className, cl); } return ca; } private Object[] params(Object cmd, CliContext ctx) { if (0 == paramCount) { return DUMP_PARAMS; } return paramLoaderService.loadMethodParams(cmd, method, ctx); } private Object invoke(Object commander, Object[] params) { Object result; if (null != methodAccess) { try { result = methodAccess.invoke(commander, commandIndex, params); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw E.unexpected(e); } } else { try { result = method.invoke(null, params); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw E.unexpected(e); } } return result; } private void buildParsingContext() { CliContextParamLoader loader = (CliContextParamLoader) app.service(ParamValueLoaderManager.class).get(CliContext.class); this.parsingContext = loader.buildParsingContext(commanderClass, method, methodMetaInfo); } }