/******************************************************************************* * Copyright (c) 2008, 2014 Stuart McCulloch * 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: * Stuart McCulloch - initial API and implementation *******************************************************************************/ package org.eclipse.sisu.peaberry.internal; import static org.eclipse.sisu.peaberry.internal.Setting.newSetting; import static org.eclipse.sisu.peaberry.internal.Setting.nullSetting; import java.util.Map; import org.eclipse.sisu.peaberry.AttributeFilter; import org.eclipse.sisu.peaberry.Export; import org.eclipse.sisu.peaberry.Import; import org.eclipse.sisu.peaberry.ServiceRegistry; import org.eclipse.sisu.peaberry.ServiceWatcher; import org.eclipse.sisu.peaberry.builders.ImportDecorator; import org.eclipse.sisu.peaberry.util.Filters; import org.eclipse.sisu.peaberry.util.StaticImport; import com.google.inject.Injector; import com.google.inject.Key; /** * Maintain state of {@link ServiceBuilderImpl} while the fluent API is used. * Also includes a few helpers to simplify the import and export of services. * * @author mcculls@gmail.com (Stuart McCulloch) */ final class ServiceSettings<T> implements Cloneable { // initial constant settings private final Setting<T> service; private final Class<T> clazz; // current builder state... private Setting<ServiceRegistry> registry = newSetting(Key.get(ServiceRegistry.class)); private Setting<ImportDecorator<? super T>> decorator = nullSetting(); private Setting<ServiceWatcher<? super T>> watcher = nullSetting(); private Setting<Map<String, ?>> attributes = nullSetting(); private Setting<AttributeFilter> filter = nullSetting(); /** * Configure service based on binding key. */ @SuppressWarnings("unchecked") ServiceSettings(final Key<? extends T> key) { service = newSetting(key); clazz = (Class) key.getTypeLiteral().getRawType(); } /** * Configure service based on explicit instance. */ @SuppressWarnings("unchecked") ServiceSettings(final T instance) { if (null == instance) { service = nullSetting(); clazz = (Class) Object.class; } else { service = newSetting(instance); clazz = (Class) instance.getClass(); } } // setters... void setDecorator(final Setting<ImportDecorator<? super T>> decorator) { this.decorator = decorator; } void setAttributes(final Setting<Map<String, ?>> attributes) { this.attributes = attributes; } void setFilter(final Setting<AttributeFilter> filter) { this.filter = filter; } void setWatcher(final Setting<ServiceWatcher<? super T>> watcher) { this.watcher = watcher; } void setRegistry(final Setting<ServiceRegistry> registry) { this.registry = registry; } // helper methods... @Override @SuppressWarnings("unchecked") public ServiceSettings<T> clone() { try { // clone all settings to preserve state return (ServiceSettings<T>) super.clone(); } catch (final CloneNotSupportedException e) { // /CLOVER:OFF return this; // /CLOVER:ON } } // query methods... Class<T> getClazz() { return clazz; } ImportDecorator<? super T> getDecorator(final Injector injector) { return decorator.get(injector); } private AttributeFilter getFilter(final Injector injector) { final AttributeFilter attributeFilter = filter.get(injector); if (null == attributeFilter) { // no filter, try using the current attributes as a sample filter final Map<String, ?> sampleAttributes = attributes.get(injector); if (null != sampleAttributes && !sampleAttributes.isEmpty()) { filter = newSetting(Filters.attributes(sampleAttributes)); return filter.get(injector); } } return attributeFilter; } Iterable<Import<T>> getImports(final Injector injector, final boolean isConcurrent) { final ServiceRegistry serviceRegistry = registry.get(injector); final AttributeFilter attributeFilter = getFilter(injector); final Iterable<Import<T>> imports = serviceRegistry.lookup(clazz, attributeFilter); // enable outjection, but only if it's going to a different watcher ServiceWatcher<? super T> serviceWatcher = watcher.get(injector); if (null != serviceWatcher && serviceRegistry != serviceWatcher) { // NOPMD final ImportDecorator<? super T> watcherDecorator = decorator.get(injector); if (null != watcherDecorator) { // decorate the watcher if necessary, to support decorated watching serviceWatcher = new DecoratedServiceWatcher<T>(watcherDecorator, serviceWatcher); } if (isConcurrent) { // now apply concurrent behaviour when watching single services serviceWatcher = new ConcurrentServiceWatcher<T>(imports, serviceWatcher); } serviceRegistry.watch(clazz, attributeFilter, serviceWatcher); } return imports; } private Export<T> export; // workaround issue 35: Guice calls twice by mistake Export<T> getExport(final Injector injector) { // this might cause a reentrant call... final T instance = service.get(injector); if (null == export) { // watcher might be null, but registry setting will be non-null ServiceWatcher<? super T> serviceWatcher = watcher.get(injector); if (null == serviceWatcher) { serviceWatcher = registry.get(injector); } // decorate the watcher if necessary, to support decorated exports final ImportDecorator<? super T> watcherDecorator = decorator.get(injector); if (null != watcherDecorator) { serviceWatcher = new DecoratedServiceWatcher<T>(watcherDecorator, serviceWatcher); } export = serviceWatcher.add(new StaticImport<T>(instance, attributes.get(injector))); } return export; } }