/* * Copyright 2012 astamuse company,Ltd. * * 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. * */ package com.astamuse.asta4d.snippet.resolve; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.astamuse.asta4d.Configuration; import com.astamuse.asta4d.Context; import com.astamuse.asta4d.render.Renderer; import com.astamuse.asta4d.snippet.SnippetDeclarationInfo; import com.astamuse.asta4d.snippet.SnippetExcecutionInfo; import com.astamuse.asta4d.snippet.SnippetNotResovlableException; import com.astamuse.asta4d.util.MultiSearchPathResourceLoader; public class DefaultSnippetResolver extends MultiSearchPathResourceLoader<Object> implements SnippetResolver { private final static String InstanceMapCacheKey = DefaultSnippetResolver.class.getName() + "##InstanceMapCacheKey"; private final static ConcurrentHashMap<SnippetDeclarationInfo, Method> MethodCache = new ConcurrentHashMap<>(); @Override public SnippetExcecutionInfo resloveSnippet(SnippetDeclarationInfo declaration) throws SnippetNotResovlableException { Object instance = retrieveInstance(declaration); Method method = retrieveMethod(declaration); return new SnippetExcecutionInfo(declaration, instance, method); } protected Object retrieveInstance(SnippetDeclarationInfo declaration) throws SnippetNotResovlableException { String snippetName = declaration.getSnippetName(); Map<String, Object> instanceMap = getCacheMap(InstanceMapCacheKey); Object instance = instanceMap.get(snippetName); if (instance == null) { instance = createInstance(snippetName); instanceMap.put(snippetName, instance); } return instance; } protected Object createInstance(String snippetName) throws SnippetNotResovlableException { try { Object instance = super.searchResource(".", snippetName); if (instance == null) { throw new ClassNotFoundException("Can not found class for snippet name:" + snippetName); } return instance; } catch (Exception ex) { throw new SnippetNotResovlableException(String.format("Snippet [%s] resolve failed.", snippetName), ex); } } @Override protected Object loadResource(String name) { Class<?> clz = null; try { clz = Class.forName(name); } catch (ClassNotFoundException e) { return null; } try { return clz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } protected Method retrieveMethod(SnippetDeclarationInfo declaration) throws SnippetNotResovlableException { Method m = Configuration.getConfiguration().isCacheEnable() ? MethodCache.get(declaration) : null; if (m == null) { Object instance = retrieveInstance(declaration); m = findSnippetMethod(instance, declaration.getSnippetHandler()); if (m == null) { throw new SnippetNotResovlableException("Snippet handler cannot be resolved for " + declaration); } // we do not mind that the exited method instance would be // overrode in multi-threads environment MethodCache.put(declaration, m); } return m; } protected Method findSnippetMethod(Object snippetInstance, String methodName) { Method[] methodList = snippetInstance.getClass().getMethods(); Class<?> rendererCls = Renderer.class; List<Method> namedMtdList = new ArrayList<>(); List<Method> priorMtdList = new ArrayList<>(); for (Method method : methodList) { if (method.getName().equals(methodName) && rendererCls.isAssignableFrom(method.getReturnType())) { namedMtdList.add(method); if (method.getAnnotation(PriorRenderMethod.class) != null) { priorMtdList.add(method); } } } if (priorMtdList.isEmpty()) { if (namedMtdList.isEmpty()) { return null; } else { return namedMtdList.get(namedMtdList.size() - 1); } } else { return priorMtdList.get(priorMtdList.size() - 1); } } private Map<String, Object> getCacheMap(String key) { Context context = Context.getCurrentThreadContext(); Map<String, Object> map = context.getData(key); if (map == null) { map = new HashMap<>(); context.setData(key, map); } return map; } }