Java tutorial
/* * Copyright (C) 2013 salesforce.com, inc. * * 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 org.auraframework.util; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Set; import org.auraframework.ds.serviceloader.AuraServiceProvider; import org.auraframework.util.ServiceLocator.ServiceLocatorException; import org.reflections.ReflectionUtils; import org.reflections.Reflections; import org.reflections.scanners.MethodAnnotationsScanner; import org.reflections.scanners.TypeAnnotationsScanner; import org.reflections.scanners.TypesScanner; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; import org.reflections.util.FilterBuilder; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Sets; /** * @since 0.0.233 */ public class ServiceLoaderImpl implements ServiceLoader { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface AuraConfiguration { } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Impl { String name() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface PrimaryImpl { } private static final ServiceLoader instance = new ServiceLoaderImpl(); @SuppressWarnings("unchecked") private static final Predicate<? super Method> predicate = Predicates.and( ReflectionUtils.withModifier(Modifier.PUBLIC), ReflectionUtils.withAnnotation(Impl.class), ReflectionUtils.withModifier(Modifier.STATIC), ReflectionUtils.withParametersCount(0)); private final Reflections reflections; private ServiceLoaderImpl() { Predicate<String> filter = new FilterBuilder().include(FilterBuilder.prefix("configuration")); reflections = new Reflections(new ConfigurationBuilder().filterInputsBy(filter) .setUrls(ClasspathHelper.forPackage("configuration")) .setScanners(new TypeAnnotationsScanner(), new MethodAnnotationsScanner(), new TypesScanner())); } public static final ServiceLoader get() { return instance; } @Override public <T extends AuraServiceProvider> T get(Class<T> type) { try { Set<Class<?>> classes = reflections.getTypesAnnotatedWith(AuraConfiguration.class); // First try those marked with primary T ret = get(type, classes, true, predicate); if (ret != null) { return ret; } return get(type, classes, false, predicate); } catch (Throwable t) { throw new ServiceLocatorException(t); } } @SuppressWarnings("unchecked") private <T> T get(Class<T> type, Set<Class<?>> classes, boolean primary, Predicate<? super Method> predicate) { Set<Method> beanMethods = Sets.newHashSet(); Predicate<Method> pred; pred = Predicates.and(predicate, ReflectionUtils.withReturnTypeAssignableTo(type)); if (primary) { pred = Predicates.and(pred, ReflectionUtils.withAnnotation(PrimaryImpl.class)); } /* * This is a better way to do it, but hits a runtime dep on Guava 12, so * until we upgrade to Guava 12, working around this. */ for (Class<?> clazz : classes) { for (Method meth : clazz.getDeclaredMethods()) { if (pred.apply(meth)) { beanMethods.add(meth); } } } T ret = null; try { for (Method meth : beanMethods) { T tmp = (T) meth.invoke(null); if (tmp != null) { if (ret != null) { throw new ServiceLocatorException( "More than one implementation found (primary=" + primary + ")."); } ret = tmp; } } } catch (Exception e) { throw new ServiceLocatorException(e); } return ret; } @SuppressWarnings("unchecked") @Override public <T extends AuraServiceProvider> Set<T> getAll(Class<T> type) { Set<Method> beanMethods = Sets.newHashSet(); Set<T> ret = Sets.newHashSet(); Predicate<Method> pred = Predicates.and(predicate, ReflectionUtils.withReturnTypeAssignableTo(type)); Set<Class<?>> classes = reflections.getTypesAnnotatedWith(AuraConfiguration.class); /* * This is a better way to do it, but hits a runtime dep on Guava 12, so * until we upgrade to Guava 12, working around this. */ for (Class<?> clazz : classes) { for (Method meth : clazz.getDeclaredMethods()) { if (pred.apply(meth)) { beanMethods.add(meth); } } } try { for (Method meth : beanMethods) { T val = (T) meth.invoke(null); if (val != null) { ret.add(val); } } } catch (Exception e) { throw new ServiceLocatorException(e); } return ret; } @Override public <T extends AuraServiceProvider> T get(Class<T> type, final String name) { try { Predicate<? super Method> predicate = Predicates.and(ServiceLoaderImpl.predicate, new Predicate<Method>() { @Override public boolean apply(Method input) { return input.getAnnotation(Impl.class).name().equals(name); } }); Set<Class<?>> classes = reflections.getTypesAnnotatedWith(AuraConfiguration.class); // First try those marked with primary T ret = get(type, classes, true, predicate); if (ret != null) { return ret; } return get(type, classes, false, predicate); } catch (Throwable t) { throw new ServiceLocatorException(t); } } }