/* * Copyright 2014 Red Hat, Inc. and/or its affiliates. * * Licensed under the Eclipse Public License version 1.0, available at * http://www.eclipse.org/legal/epl-v10.html */ package org.jboss.forge.furnace.container.cdi.impl; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.Callable; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.inject.Qualifier; import org.jboss.forge.furnace.addons.Addon; import org.jboss.forge.furnace.container.cdi.services.ExportedInstanceImpl; import org.jboss.forge.furnace.exception.ContainerException; import org.jboss.forge.furnace.lock.LockManager; import org.jboss.forge.furnace.spi.ExportedInstance; import org.jboss.forge.furnace.spi.ServiceRegistry; import org.jboss.forge.furnace.util.Addons; import org.jboss.forge.furnace.util.Assert; import org.jboss.forge.furnace.util.ClassLoaders; /** * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a> */ public class WeldServiceRegistry implements ServiceRegistry { private final Class<?>[] serviceTypes; private final BeanManager manager; private final Addon addon; private final Set<Class<?>> servicesSet; private final Map<String, ExportedInstance<?>> instanceCache = new WeakHashMap<>(); private final Map<String, Set<ExportedInstance<?>>> instancesCache = new WeakHashMap<>(); public WeldServiceRegistry(LockManager lock, Addon addon, BeanManager manager, Set<Class<?>> services) { this.addon = addon; this.manager = manager; // Copy set to avoid any reference pointers Set<Class<?>> copy = new LinkedHashSet<>(); copy.addAll(services); this.serviceTypes = new ArrayList<>(copy).toArray(new Class<?>[copy.size()]); this.servicesSet = new LinkedHashSet<>(Arrays.asList(this.serviceTypes)); } @Override @SuppressWarnings("unchecked") public <T> ExportedInstance<T> getExportedInstance(String clazz) { try { return getExportedInstance((Class<T>) Class.forName(clazz, false, addon.getClassLoader())); } catch (ClassNotFoundException | LinkageError e) { return null; } } @Override @SuppressWarnings("unchecked") public <T> ExportedInstance<T> getExportedInstance(final Class<T> clazz) { Assert.notNull(clazz, "Requested Class type may not be null"); Addons.waitUntilStarted(addon); ExportedInstance<T> result = (ExportedInstance<T>) instanceCache.get(clazz.getName()); if (result == null) { try { result = ClassLoaders.executeIn(addon.getClassLoader(), new Callable<ExportedInstance<T>>() { @Override public ExportedInstance<T> call() throws Exception { Set<Bean<?>> beans = manager.getBeans(clazz, getQualifiersFrom(clazz)); if (!beans.isEmpty()) { ExportedInstance<T> result = new ExportedInstanceImpl<>( addon, manager, (Bean<T>) manager.resolve(beans), clazz, clazz); instanceCache.put(clazz.getName(), result); return result; } return null; } }); } catch (Exception e) { throw new ContainerException("Could not get service of type [" + clazz + "] from addon [" + addon + "]", e); } } return result; } @Override public boolean hasService(String clazz) { try { return hasService(Class.forName(clazz, false, addon.getClassLoader())); } catch (ClassNotFoundException | LinkageError e) { return false; } } @Override public boolean hasService(Class<?> clazz) { Addons.waitUntilStarted(addon); for (Class<?> service : serviceTypes) { if (clazz.isAssignableFrom(service)) { return true; } } return false; } @Override @SuppressWarnings("unchecked") public <T> Set<ExportedInstance<T>> getExportedInstances(String clazz) { try { return getExportedInstances((Class<T>) Class.forName(clazz, false, addon.getClassLoader())); } catch (ClassNotFoundException | LinkageError e) { return Collections.emptySet(); } } @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public <T> Set<ExportedInstance<T>> getExportedInstances(final Class<T> clazz) { Addons.waitUntilStarted(addon); Set<ExportedInstance<T>> result = (Set) instancesCache.get(clazz.getName()); if (result == null || result.isEmpty()) { result = new HashSet<>(); for (int i = 0; i < serviceTypes.length; i++) { final Class<?> type = serviceTypes[i]; if (clazz.isAssignableFrom(type)) { try { result.addAll(ClassLoaders.executeIn(addon.getClassLoader(), new Callable<Set<ExportedInstance<T>>>() { @Override public Set<ExportedInstance<T>> call() throws Exception { Set<ExportedInstance<T>> result = new HashSet<>(); Set<Bean<?>> beans = manager.getBeans(type, getQualifiersFrom(type)); Class<? extends T> assignableClass = (Class<? extends T>) type; for (Bean<?> bean : beans) { result.add(new ExportedInstanceImpl<>( addon, manager, (Bean<T>) bean, clazz, assignableClass)); } return result; } })); } catch (Exception e) { throw new ContainerException("Could not get services of type [" + clazz + "] from addon [" + addon + "]", e); } } instancesCache.put(clazz.getName(), (Set) result); } } return result; } @Override public Set<Class<?>> getExportedTypes() { return Collections.unmodifiableSet(servicesSet); } @Override @SuppressWarnings("unchecked") public <T> Set<Class<T>> getExportedTypes(Class<T> type) { Set<Class<T>> result = new HashSet<>(); for (Class<?> serviceType : serviceTypes) { if (type.isAssignableFrom(serviceType)) result.add((Class<T>) serviceType); } return result; } @Override public String toString() { return "WeldServiceRegistry [serviceTypes=" + Arrays.toString(serviceTypes) + ", addon=" + addon + "]"; } /** * Returns the annotation qualifiers from a type */ public static Annotation[] getQualifiersFrom(final Class<?> c) { Set<Annotation> annotations = new HashSet<>(); for (Annotation annotation : c.getAnnotations()) { if (annotation.annotationType().isAnnotationPresent(Qualifier.class)) { annotations.add(annotation); } } return annotations.toArray(new Annotation[annotations.size()]); } @Override public void close() { servicesSet.clear(); instanceCache.clear(); instancesCache.clear(); } }