/*******************************************************************************
* 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;
}
}