/*
*
* * Copyright (c) 2016. David Sowerby
* *
* * 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 uk.q3c.krail.core.services;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;
import uk.q3c.krail.core.config.ConfigurationException;
import uk.q3c.krail.core.i18n.I18NKey;
import javax.annotation.Nonnull;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.inject.multibindings.Multibinder.newSetBinder;
/**
* A useful base class which can be used where the developer wishes to declare dependencies between Service classes using Guice.
* Remember to invoke super.configure from the sub-class configure()
* <p>
* Created by David Sowerby on 13/11/15.
*/
public abstract class AbstractServiceModule extends AbstractModule {
TypeLiteral<Class<? extends Service>> serviceClassLiteral;
private Multibinder<DependencyDefinition> dependencies;
//other modules may add to this
private MapBinder<ServiceKey, Service> registeredServices;
private MapBinder<ServiceKey, Class<? extends Service>> serviceKeyMap;
@Override
protected void configure() {
dependencies = newSetBinder(binder(), DependencyDefinition.class);
//use TypeLiteral for one parameter have to use it for both
serviceClassLiteral = new TypeLiteral<Class<? extends Service>>() {
};
TypeLiteral<ServiceKey> serviceKeyLiteral = new TypeLiteral<ServiceKey>() {
};
registeredServices = MapBinder.newMapBinder(binder(), ServiceKey.class, Service.class);
serviceKeyMap = MapBinder.newMapBinder(binder(), serviceKeyLiteral, serviceClassLiteral);
registerServices();
defineDependencies();
}
protected abstract void registerServices();
protected abstract void defineDependencies();
/**
* Define a dependency. Dependencies added here are injected into {@link ServicesModel}
*/
@SuppressWarnings("Duplicates")
protected void addDependency(@Nonnull ServiceKey dependant, @Nonnull ServiceKey dependency, @Nonnull Dependency.Type type) {
checkNotNull(dependant);
checkNotNull(dependency);
checkNotNull(type);
configCheck();
dependencies.addBinding()
.toInstance(new DependencyDefinition(dependant, dependency, type));
}
private void configCheck() {
if (registeredServices == null || dependencies == null || serviceKeyMap == null) {
throw new ConfigurationException("MapBinder and MultiBinder fields cannot be null, have you sub-classed " + this.getClass()
.getSimpleName() + " but " +
"forgotten to call super.configure():");
}
}
@SuppressWarnings("Duplicates")
protected void addDependency(@Nonnull I18NKey dependant, @Nonnull I18NKey dependency, @Nonnull Dependency.Type type) {
checkNotNull(dependant);
checkNotNull(dependency);
checkNotNull(type);
configCheck();
dependencies.addBinding()
.toInstance(new DependencyDefinition(dependant, dependency, type));
}
/**
* Each service must be registered so that Guice can instantiate it. Call this method (or just provide your own bindings) to register a Service.
*
* @param serviceKey the class of Service to be registered
*/
protected void registerService(@Nonnull ServiceKey serviceKey, @Nonnull Class<? extends Service> serviceClass) {
checkNotNull(serviceKey);
checkNotNull(serviceClass);
configCheck();
registeredServices.addBinding(serviceKey)
.to(serviceClass);
serviceKeyMap.addBinding(serviceKey)
.toInstance(serviceClass);
}
protected void registerService(@Nonnull I18NKey key, @Nonnull Class<? extends Service> serviceClass) {
checkNotNull(key);
checkNotNull(serviceClass);
configCheck();
registerService(new ServiceKey(key), serviceClass);
}
}