/* * * * 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.ui; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Provider; import com.vaadin.server.UIClassSelectionEvent; import com.vaadin.server.UICreateEvent; import com.vaadin.server.UIProvider; import com.vaadin.ui.UI; import com.vaadin.util.CurrentInstance; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.q3c.krail.core.guice.uiscope.UIKey; import uk.q3c.krail.core.guice.uiscope.UIKeyProvider; import uk.q3c.krail.core.guice.uiscope.UIScope; import uk.q3c.krail.core.guice.uiscope.UIScoped; import java.io.Serializable; import java.util.Map; /** * A Vaadin UI provider which supports the use of Guice scoped UI (see {@link UIScoped}). If you do not need UIScope, * then just extend from UIProvider directly * <p> * Subclasses should implement getUIClass(UIClassSelectionEvent event) to provide logic for selecting the UI class. * * @author David Sowerby, Will Temperley */ @SuppressFBWarnings("SE_BAD_FIELD") public class ScopedUIProvider extends UIProvider implements Provider<ScopedUI>, Serializable { private static Logger log = LoggerFactory.getLogger(ScopedUIProvider.class); protected UIKeyProvider uiKeyProvider; protected Injector injector; private Map<String, Class<? extends ScopedUI>> uiMapBinder; @Inject protected void init(Injector injector, UIKeyProvider uiKeyProvider, Map<String, Class<? extends ScopedUI>> uiMapBinder) { this.injector = injector; this.uiKeyProvider = uiKeyProvider; this.uiMapBinder = uiMapBinder; } /** * Default implementation assumes that only one ScopedUI implementation is used in the application, and it is held in {@link #uiMapBinder}. If more than * entry is present in {@link #uiMapBinder}, the selection is indeterminate, and a warning is logged. * <p> * Override this method to provide logic for selecting from multiple UIs, then change the binging for ScopedUIProvider in a sub-class of {@link * DefaultUIModule} * * @param event * the event which triggers the selection of a UI * * @return the class of the selected UI; in this implementation, this is the only or first selected from {@link #uiMapBinder} */ @Override public Class<? extends UI> getUIClass(UIClassSelectionEvent event) { if (uiMapBinder.isEmpty()) { throw new UIProviderException("At least one UI must be defined in the UIModule, uiBinder"); } if (uiMapBinder.size() > 1) { log.warn("More than one UI class has been defined, but there is no logic to determine which to use."); } return uiMapBinder.get(uiMapBinder.keySet() .iterator() .next()); } @Override public UI createInstance(UICreateEvent event) { Class<? extends UI> uiClass = event.getUIClass(); UIKey uiKey = uiKeyProvider.get(); // hold the key while UI is created CurrentInstance.set(UIKey.class, uiKey); // and set up the scope UIScope scope = UIScope.getCurrent(); scope.startScope(uiKey); // create the UI ScopedUI ui = (ScopedUI) injector.getInstance(uiClass); ui.setInstanceKey(uiKey); ui.setScope(scope); log.debug("Returning instance of {} with key {}", uiClass.getName(), uiKey); return ui; } @Override public ScopedUI get() { return (ScopedUI) UI.getCurrent(); } }