/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.inject.lifecycle; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.inject.AbstractModule; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; /** @author andrew00x */ abstract class LifecycleModule extends AbstractModule { private static class Key { final Class<?> type; final Class<? extends Annotation> annotationType; final int hashCode; static Key of(Class<?> type, Class<? extends Annotation> annotationType) { return new Key(type, annotationType); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Key)) { return false; } Key key = (Key)o; return annotationType.equals(key.annotationType) && type.equals(key.type); } @Override public int hashCode() { return hashCode; } private Key(Class<?> type, Class<? extends Annotation> annotationType) { this.type = type; this.annotationType = annotationType; int hash = annotationType.hashCode(); hash = 31 * hash + type.hashCode(); this.hashCode = hash; } } private final LoadingCache<Key, Method[]> cache; @SuppressWarnings("unchecked") LifecycleModule() { cache = CacheBuilder.<Key, Method[]>newBuilder() .maximumSize(1_000) .expireAfterWrite(1, TimeUnit.HOURS) .build(new CacheLoader<Key, Method[]>() { @Override public Method[] load(Key key) throws Exception { return doGet(key.type, key.annotationType); } }); } Method[] get(Class<?> type, Class<? extends Annotation> annotationType) { final Key key = Key.of(type, annotationType); try { return cache.get(key); } catch (ExecutionException e) { //should never happen throw new RuntimeException(e.getLocalizedMessage(), e); } } private Method[] doGet(Class<?> type, Class<? extends Annotation> annotationType) { final List<Method> allMethods = getAllMethods(type); final LinkedList<Method> methods = new LinkedList<>(); final Set<String> methodNames = new HashSet<>(); for (Method method : allMethods) { if (method.isAnnotationPresent(annotationType) && method.getParameterTypes().length == 0 && method.getReturnType() == void.class && methodNames.add(method.getName())) { method.setAccessible(true); methods.addFirst(method); } } return methods.toArray(new Method[methods.size()]); } private List<Method> getAllMethods(Class<?> c) { final List<Method> list = new ArrayList<>(); while (c != null && c != Object.class) { Collections.addAll(list, c.getDeclaredMethods()); c = c.getSuperclass(); } return list; } }