/*
*
* * 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.guice;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.servlet.GuiceServletContextListener;
import com.google.inject.servlet.ServletModule;
import com.google.inject.util.Modules;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.bval.guice.ValidationModule;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.q3c.krail.core.config.ApplicationConfigurationModule;
import uk.q3c.krail.core.data.DataModule;
import uk.q3c.krail.core.eventbus.EventBusModule;
import uk.q3c.krail.core.guice.threadscope.ThreadScopeModule;
import uk.q3c.krail.core.guice.uiscope.UIScopeModule;
import uk.q3c.krail.core.guice.vsscope.VaadinSessionScopeModule;
import uk.q3c.krail.core.i18n.I18NModule;
import uk.q3c.krail.core.navigate.NavigationModule;
import uk.q3c.krail.core.navigate.sitemap.MasterSitemap;
import uk.q3c.krail.core.navigate.sitemap.SitemapModule;
import uk.q3c.krail.core.navigate.sitemap.StandardPagesModule;
import uk.q3c.krail.core.option.InMemory;
import uk.q3c.krail.core.option.OptionModule;
import uk.q3c.krail.core.persist.inmemory.common.InMemoryModule;
import uk.q3c.krail.core.push.PushModule;
import uk.q3c.krail.core.services.ServicesModel;
import uk.q3c.krail.core.services.ServicesModule;
import uk.q3c.krail.core.shiro.DefaultShiroModule;
import uk.q3c.krail.core.shiro.ShiroVaadinModule;
import uk.q3c.krail.core.shiro.aop.KrailShiroAopModule;
import uk.q3c.krail.core.ui.DataTypeModule;
import uk.q3c.krail.core.ui.DefaultUIModule;
import uk.q3c.krail.core.user.UserModule;
import uk.q3c.krail.core.validation.KrailValidationModule;
import uk.q3c.krail.core.view.ViewModule;
import uk.q3c.krail.core.view.component.DefaultComponentModule;
import uk.q3c.krail.util.UtilsModule;
import javax.servlet.ServletContextEvent;
import java.util.ArrayList;
import java.util.List;
/**
* Collects together all the modules used for bindings, and passes them to the {@link Injector} during the injector creation process. The separation into
* different groupings of modules is for clarity only - they do not have to be separate for any other reason.
*/
public abstract class DefaultBindingManager extends GuiceServletContextListener {
//Visible for testing
static Injector injector;
private static Logger log = LoggerFactory.getLogger(DefaultBindingManager.class);
protected DefaultBindingManager() {
super();
}
public static Injector injector() {
return injector;
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
log.info("Stopping services");
try {
if (injector != null) {
injector.getInstance(ServicesModel.class)
.stopAllServices();
} else {
log.debug("Injector has not been constructed, no call made to stop services");
}
} catch (Exception e) {
log.error("Exception while stopping services", e);
}
//context may not have been crated, and super does not check for it
if (servletContextEvent.getServletContext() != null) {
super.contextDestroyed(servletContextEvent);
}
}
/**
* Module instances for the core should be added in {@link #getModules()}. Module instances for the app using Krail
* should be added to {@link #addAppModules(List)}
*
* @see com.google.inject.servlet.GuiceServletContextListener#getInjector()
*/
@Override
public Injector getInjector() {
if (injector == null) {
createInjector();
}
return injector;
}
protected void createInjector() {
injector = Guice.createInjector(getModules());
log.debug("injector created");
// By default Shiro provides a binding to DefaultSecurityManager, but that is replaced by a binding to
// KrailSecurityManager in {@link DefaultShiroModule#bindSecurityManager} (or potentially to another security manager if
// the developer overrides that method)
SecurityManager securityManager = injector.getInstance(SecurityManager.class);
SecurityUtils.setSecurityManager(securityManager);
}
private List<Module> getModules() {
List<Module> coreModules = new ArrayList<>(30);
coreModules.add(uiModule());
coreModules.add(i18NModule());
coreModules.add(applicationConfigurationModule());
coreModules.add(new SitemapModule());
coreModules.add(new ThreadScopeModule());
coreModules.add(new UIScopeModule());
coreModules.add(new VaadinSessionScopeModule());
coreModules.add(new ServicesModule());
coreModules.add(shiroModule());
coreModules.add(shiroVaadinModule());
coreModules.add(shiroAopModule());
coreModules.add(servletModule());
coreModules.add(standardPagesModule());
coreModules.add(viewModule());
coreModules.add(componentModule());
coreModules.add(userModule());
coreModules.add(optionModule());
coreModules.add(eventBusModule());
coreModules.add(navigationModule());
coreModules.add(dataModule());
coreModules.add(dataTypeModule());
coreModules.add(pushModule());
addUtilModules(coreModules);
addValidationModules(coreModules);
addAppModules(coreModules);
addSitemapModules(coreModules);
addPersistenceModules(coreModules);
return coreModules;
}
protected void addUtilModules(List<Module> coreModules){
coreModules.add(new UtilsModule());
}
protected Module shiroAopModule() {
return new KrailShiroAopModule();
}
/**
* Override this if you have provided your own {@link DataTypeModule} implementation
*
* @return a new {@link DataTypeModule} instance
*/
protected Module dataTypeModule() {
return new DataTypeModule();
}
/**
* Override this if you have provided your own {@link PushModule} implementation
*
* @return a new {@link PushModule} instance
*/
protected Module pushModule() {
return new PushModule();
}
protected Module uiModule() {
return new DefaultUIModule();
}
/**
* Override this if you have provided your own {@link DataModule} implementation
*
* @return a new {@link DataModule} instance
*/
protected Module dataModule() {
return new DataModule();
}
/**
* Override this if you have provided your own {@link EventBusModule} implementation
*
* @return a new {@link EventBusModule} instance
*/
protected Module eventBusModule() {
return new EventBusModule();
}
/**
* Override this if you have provided your own {@link NavigationModule}
*
* @return new instance of ApplicationConfigurationModule
*/
protected AbstractModule navigationModule() {
return new NavigationModule();
}
/**
* Override this method if you want to use an alternative implementation for the Krail validation integration. You
* will need to keep the Apache Bval {{@link ValidationModule} unless you replace the the javax validation
* implementation.
*
* @param modules
* the list used to collect modules for injector creation
*/
protected void addValidationModules(List<Module> modules) {
final Module validationModule = Modules.override(new ValidationModule())
.with(new KrailValidationModule());
modules.add(validationModule);
}
/**
* Sets the default active source to read/write Option values from / to the in memory store
*
* Override this if you have provided your own {@link OptionModule} or want to change the active source
*
* @return module instance
*/
protected Module optionModule() {
return new OptionModule().activeSource(InMemory.class);
}
/**
* Override this if you have provided your own {@link I18NModule}
*
* @return a Module fr I18N
*/
protected Module i18NModule() {
return new I18NModule();
}
/**
* Override this if you have provided your own {@link ApplicationConfigurationModule}
*
* @return new instance of ApplicationConfigurationModule
*/
protected Module applicationConfigurationModule() {
return new ApplicationConfigurationModule();
}
/**
* Modules used in the creation of the {@link MasterSitemap} do not actually need to be separated, this just makes a convenient way of seeing them as a
* group
*
* @param modules
* the list used to collect modules for injector creation
*/
@SuppressFBWarnings("ACEM_ABSTRACT_CLASS_EMPTY_METHODS")
protected void addSitemapModules(List<Module> modules) {
}
protected Module componentModule() {
return new DefaultComponentModule();
}
/**
* Override this if you have provided your own {@link ServletModule}
*
* @return servlet module instance
*/
protected Module servletModule() {
return new BaseServletModule();
}
/**
* Override this method if you have sub-classed {@link ShiroVaadinModule} to provide your own bindings for Shiro
* related exceptions.
*
* @return a module for bindings which realte to Shiro wihtin a Vaadin environment
*/
protected Module shiroVaadinModule() {
return new ShiroVaadinModule();
}
/**
* Override this if you have sub-classed {@link StandardPagesModule} to provide bindings to your own standard page
* views
*/
protected Module standardPagesModule() {
return new StandardPagesModule();
}
/**
* Override this if you have sub-classed {@link ViewModule} to provide bindings to your own standard page views
*/
protected Module viewModule() {
return new ViewModule();
}
/**
* Override this method if you have sub-classed {@link DefaultShiroModule} to provide bindings to your Shiro
* related implementations (for example, {@link Realm} and {@link CredentialsMatcher}
*
* @return a new {@link DefaultShiroModule} instance
*/
protected Module shiroModule() {
return new DefaultShiroModule();
}
/**
* Override this if you have sub-classed {@link UserModule} to provide bindings to your user related
* implementations
*
* @return a new instance of {@link UserModule} or sub-class
*/
protected UserModule userModule() {
return new UserModule();
}
/**
* Add as many application specific Guice modules as you wish by overriding this method.
*
* @param modules
* the list used to collect modules for injector creation
*/
protected abstract void addAppModules(List<Module> modules);
/**
* Add as many persistence related modules as needed. These modules do not need to be separated, this just forms a convneient grouping for clarity
*
* @param modules
* the list used to collect modules for injector creation
*/
protected void addPersistenceModules(List<Module> modules) {
modules.add(new InMemoryModule().provideOptionDao()
.providePatternDao());
}
}