/* * Copyright 2016 Guillaume Nodet * * 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.ops4j.pax.cdi.extension.impl.component2; import javax.enterprise.context.Dependent; import javax.enterprise.inject.Instance; import javax.enterprise.inject.spi.Annotated; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.InjectionPoint; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.Supplier; import org.apache.felix.scr.impl.metadata.ReferenceMetadata; import org.apache.felix.scr.impl.metadata.ServiceMetadata; import org.ops4j.pax.cdi.api.*; import org.ops4j.pax.cdi.extension.impl.support.Filters; import org.ops4j.pax.cdi.extension.impl.support.IterableInstance; import org.ops4j.pax.cdi.extension.impl.support.PrivateRegistryWrapper; import org.ops4j.pax.cdi.extension.impl.support.SimpleBean; import org.ops4j.pax.cdi.extension.impl.support.Types; import org.ops4j.pax.cdi.spi.CdiContainer; import org.ops4j.pax.cdi.spi.CdiContainerFactory; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.framework.wiring.BundleWiring; import org.osgi.service.component.ComponentContext; public class GlobalDescriptor extends AbstractDescriptor { private final ComponentRegistry registry; private final Map<InjectionPoint, Supplier<Object>> instanceSuppliers = new HashMap<>(); private final List<Bean<?>> producers = new ArrayList<>(); private CdiContainer container; private ComponentContext context; public GlobalDescriptor(ComponentRegistry registry) { super(registry); this.registry = registry; ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.addProvide(Object.class.getName()); serviceMetadata.setScope("singleton"); setName(UUID.randomUUID().toString()); setImmediate(true); setImplementationClassName(Object.class.getName()); setConfigurationPolicy(CONFIGURATION_POLICY_IGNORE); getProperties().put(GlobalDescriptor.class.getName(), this); getProperties().put(ComponentRegistry.class.getName(), registry); setService(serviceMetadata); } public void pauseIfNeeded() { if (!getProducers().isEmpty()) { BundleContext bundleContext = BundleContextHolder.getBundleContext(); ServiceReference<CdiContainerFactory> factoryRef = bundleContext.getServiceReference(CdiContainerFactory.class); CdiContainerFactory factory = bundleContext.getService(factoryRef); container = factory.getContainer(bundleContext.getBundle()); container.pause(); } } static class DummyInjectionPoint implements InjectionPoint, Annotated { private final Class<?> type; private final Map<Class<? extends Annotation>, Annotation> annotations; public DummyInjectionPoint(Class<?> type, Set<Annotation> annotations) { this.type = type; this.annotations = new HashMap<>(); for (Annotation annotation : annotations) { this.annotations.put(annotation.annotationType(), annotation); } } @Override public String toString() { return type.getName() + new ArrayList<>(annotations.values()); } @Override public Type getBaseType() { return type; } @Override public Set<Type> getTypeClosure() { return Collections.<Type>singleton(type); } @Override public <T extends Annotation> T getAnnotation(Class<T> annotationType) { return (T) annotations.get(annotationType); } @Override public Set<Annotation> getAnnotations() { return Collections.unmodifiableSet(new HashSet<>(annotations.values())); } @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { return annotations.containsKey(annotationType); } @Override public Type getType() { return type; } @Override public Set<Annotation> getQualifiers() { return getAnnotations(); } @Override public Bean<?> getBean() { return null; } @Override public Member getMember() { return null; } @Override public Annotated getAnnotated() { return this; } @Override public boolean isDelegate() { return false; } @Override public boolean isTransient() { return false; } } @SuppressWarnings("unchecked") public <T> Bean<T> addGlobalInjectionPoint(Class<T> clazz, Set<Annotation> qualifiers) { return (Bean<T>) addGlobalInjectionPoint(new DummyInjectionPoint(clazz, qualifiers)); } public Bean<?> addGlobalInjectionPoint(final InjectionPoint injectionPoint) { Service ref = injectionPoint.getAnnotated().getAnnotation(Service.class); Component cmp = injectionPoint.getAnnotated().getAnnotation(Component.class); Config cfg = injectionPoint.getAnnotated().getAnnotation(Config.class); Type type = injectionPoint.getType(); Class clazz; final boolean multiple; if (type instanceof ParameterizedType) { Type raw = ((ParameterizedType) type).getRawType(); if (raw == Instance.class) { multiple = true; clazz = (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; } else { multiple = false; clazz = (Class) ((ParameterizedType) type).getRawType(); } } else { if (type == Instance.class) { throw new IllegalArgumentException(); } multiple = false; clazz = (Class) type; } if (multiple) { throw new IllegalArgumentException("@Global Instance<?> not supported: " + injectionPoint); } if (cfg != null) { throw new IllegalArgumentException("@Config @Global not supported: " + injectionPoint); } else { List<String> subFilters = Filters.getSubFilters(injectionPoint.getAnnotated().getAnnotations()); if (ref == null) { subFilters.add("(" + PrivateRegistryWrapper.PRIVATE + "=true)"); } String filter = Filters.and(subFilters); boolean optional = injectionPoint.getAnnotated().isAnnotationPresent(Optional.class); boolean greedy = injectionPoint.getAnnotated().isAnnotationPresent(Greedy.class); final boolean dynamic = injectionPoint.getAnnotated().isAnnotationPresent(Dynamic.class); ReferenceMetadata reference = new ReferenceMetadata(); reference.setName(injectionPoint.getAnnotated().toString()); reference.setInterface(clazz.getName()); reference.setTarget(filter); reference.setCardinality(optional ? "0..1" : "1..1"); reference.setPolicy(dynamic ? "dynamic" : "static"); reference.setPolicyOption(greedy ? "greedy" : "reluctant"); addDependency(reference); Supplier<Object> supplier = new Supplier<Object>() { @Override public Object get() { return GlobalDescriptor.this.getService(injectionPoint, multiple, dynamic); } }; Bean<?> bean = new SimpleBean<>(clazz, Dependent.class, injectionPoint, supplier); producers.add(bean); instanceSuppliers.put(injectionPoint, supplier); return bean; } } public ComponentContext getComponentContext() { return context; } protected Object getService(final InjectionPoint injectionPoint, boolean isInstance, boolean dynamic) { final ComponentContext cc = context; if (cc == null) { throw new IllegalStateException("Can not obtain @Component instance"); } if (dynamic && isInstance) { Iterable<Object> iterable = new Iterable<Object>() { @Override public Iterator<Object> iterator() { return new Iterator<Object>() { final Object[] services = cc.locateServices(injectionPoint.toString()); int idx; public boolean hasNext() { return services != null && idx < services.length; } public Object next() { return services[idx++]; } }; } }; return new IterableInstance<>(iterable); } else if (isInstance) { final Object[] services = cc.locateServices(injectionPoint.toString()); Iterable<Object> iterable = new Iterable<Object>() { @Override public Iterator<Object> iterator() { return new Iterator<Object>() { int idx; public boolean hasNext() { return services != null && idx < services.length; } public Object next() { return services[idx++]; } }; } }; return new IterableInstance<>(iterable); } else if (dynamic) { Class<Object> clazz = Types.getRawType(injectionPoint.getType()); ClassLoader cl = registry.getBundleContext().getBundle().adapt(BundleWiring.class).getClassLoader(); return Proxy.newProxyInstance(cl, new Class[]{ clazz }, new InvocationHandler() { @Override public Object invoke(Object p, Method method, Object[] args) throws Throwable { Object t = cc.locateService(injectionPoint.toString()); return t != null ? method.invoke(t, args) : null; } }); } else { return cc.locateService(injectionPoint.toString()); } } public List<Bean<?>> getProducers() { return producers; } public Object activate(ComponentContext cc) { if (container != null) { container.resume(); context = cc; } return new Object(); } public void deactivate(ComponentContext cc) { new Thread() { public void run() { if (container != null) { context = null; container.stop(); container.start(new Object()); } } }.start(); } }