/* * Copyright 2008-2017 the original author or authors. * * 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 org.codehaus.griffon.runtime.core.mvc; import griffon.core.Context; import griffon.core.ContextFactory; import griffon.core.GriffonApplication; import griffon.core.artifact.GriffonController; import griffon.core.artifact.GriffonModel; import griffon.core.artifact.GriffonMvcArtifact; import griffon.core.artifact.GriffonView; import griffon.core.mvc.MVCFunction; import griffon.core.mvc.MVCGroup; import griffon.core.mvc.MVCGroupConfiguration; import griffon.core.mvc.MVCGroupConfigurationFactory; import griffon.core.mvc.MVCGroupFactory; import griffon.core.mvc.MVCGroupFunction; import griffon.core.mvc.MVCGroupManager; import griffon.exceptions.ArtifactNotFoundException; import griffon.exceptions.MVCGroupConfigurationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.inject.Inject; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static griffon.core.GriffonExceptionHandler.sanitize; import static griffon.util.GriffonNameUtils.isBlank; import static griffon.util.GriffonNameUtils.requireNonBlank; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableMap; import static java.util.Objects.requireNonNull; /** * Base implementation of the {@code MVCGroupManager} interface. * * @author Andres Almiray * @since 2.0.0 */ public abstract class AbstractMVCGroupManager implements MVCGroupManager { protected static final String ERROR_MVCTYPE_BLANK = "Argument 'mvcType' must not be blank"; protected static final String ERROR_MVCID_BLANK = "Argument 'mvcId' must not be blank"; protected static final String ERROR_CONFIGURATION_NULL = "Argument 'configuration' must not be null"; protected static final String ERROR_GROUP_NULL = "Argument 'group' must not be null"; protected static final String ERROR_CONFIG_NULL = "Argument 'config' must not be null"; protected static final String ERROR_ARGS_NULL = "Argument 'args' must not be null"; protected static final String ERROR_NAME_BLANK = "Argument 'name' cannot be blank"; protected static final String ERROR_TYPE_NULL = "Argument 'type' cannot be null"; private static final Logger LOG = LoggerFactory.getLogger(AbstractMVCGroupManager.class); private final GriffonApplication application; private final Map<String, MVCGroupConfiguration> configurations = new LinkedHashMap<>(); private final Map<String, MVCGroup> groups = new LinkedHashMap<>(); private final Object lock = new Object[0]; private boolean initialized; @Inject private MVCGroupConfigurationFactory mvcGroupConfigurationFactory; @Inject private MVCGroupFactory mvcGroupFactory; @Inject private ContextFactory contextFactory; @Inject public AbstractMVCGroupManager(@Nonnull GriffonApplication application) { this.application = requireNonNull(application, "Argument 'application' must not be null"); } @Override public GriffonApplication getApplication() { return application; } @Nonnull public MVCGroupConfiguration newMVCGroupConfiguration(@Nonnull String mvcType, @Nonnull Map<String, String> members, @Nonnull Map<String, Object> config) { return mvcGroupConfigurationFactory.create(mvcType, members, config); } @Nonnull public MVCGroup newMVCGroup(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> members, @Nullable MVCGroup parentGroup) { return mvcGroupFactory.create(configuration, mvcId, members, parentGroup); } @Nonnull @Override public Context newContext(@Nullable MVCGroup parentGroup) { Context parentContext = parentGroup != null ? parentGroup.getContext() : getApplication().getContext(); return contextFactory.create(parentContext); } @Nonnull public Map<String, MVCGroupConfiguration> getConfigurations() { synchronized (lock) { return unmodifiableMap(configurations); } } @Nonnull public Map<String, MVCGroup> getGroups() { synchronized (lock) { return unmodifiableMap(groups); } } @Nonnull public MVCGroupConfiguration findConfiguration(@Nonnull String mvcType) { requireNonBlank(mvcType, ERROR_MVCTYPE_BLANK); MVCGroupConfiguration configuration; synchronized (lock) { configuration = configurations.get(mvcType); } if (configuration == null) { throw new MVCGroupConfigurationException("Unknown MVC type '" + mvcType + "'. Known types are " + configurations.keySet(), mvcType); } return configuration; } @Nullable public MVCGroup findGroup(@Nonnull String mvcId) { requireNonBlank(mvcId, ERROR_MVCID_BLANK); synchronized (lock) { LOG.debug("Searching group {}", mvcId); return groups.get(mvcId); } } @Nullable public MVCGroup getAt(@Nonnull String mvcId) { return findGroup(mvcId); } public final void initialize(@Nonnull Map<String, MVCGroupConfiguration> configurations) { requireNonNull(configurations, "Argument 'configurations' must not be null"); if (configurations.isEmpty()) return; synchronized (lock) { if (!initialized) { doInitialize(configurations); initialized = true; } } } public void addConfiguration(@Nonnull MVCGroupConfiguration configuration) { requireNonNull(configuration, ERROR_CONFIGURATION_NULL); synchronized (lock) { if (initialized && configurations.get(configuration.getMvcType()) != null) { return; } configurations.put(configuration.getMvcType(), configuration); } } public void removeConfiguration(@Nonnull MVCGroupConfiguration configuration) { requireNonNull(configuration, ERROR_CONFIGURATION_NULL); removeConfiguration(configuration.getMvcType()); } public void removeConfiguration(@Nonnull String name) { requireNonBlank(name, "Argument 'name' must not be blank"); if (!isBlank(name)) { synchronized (lock) { configurations.remove(name); } } } protected void addGroup(@Nonnull MVCGroup group) { requireNonNull(group, ERROR_GROUP_NULL); synchronized (lock) { LOG.debug("Adding group {}:{}", group.getMvcId(), group); groups.put(group.getMvcId(), group); } } protected void removeGroup(@Nonnull MVCGroup group) { requireNonNull(group, ERROR_GROUP_NULL); synchronized (lock) { LOG.debug("Removing group {}:{}", group.getMvcId(), group); groups.remove(group.getMvcId()); } } @Nonnull public final Map<String, ? extends GriffonModel> getModels() { Map<String, GriffonModel> models = new LinkedHashMap<>(); synchronized (lock) { for (MVCGroup group : groups.values()) { GriffonModel model = group.getModel(); if (model != null) { models.put(group.getMvcId(), model); } } } return unmodifiableMap(models); } @Nonnull public final Map<String, ? extends GriffonView> getViews() { Map<String, GriffonView> views = new LinkedHashMap<>(); synchronized (lock) { for (MVCGroup group : groups.values()) { GriffonView view = group.getView(); if (view != null) { views.put(group.getMvcId(), view); } } } return unmodifiableMap(views); } @Nonnull public final Map<String, ? extends GriffonController> getControllers() { Map<String, GriffonController> controllers = new LinkedHashMap<>(); synchronized (lock) { for (MVCGroup group : groups.values()) { GriffonController controller = group.getController(); if (controller != null) { controllers.put(group.getMvcId(), controller); } } } return unmodifiableMap(controllers); } @Nonnull @Override public MVCGroupConfiguration cloneMVCGroupConfiguration(@Nonnull String mvcType, @Nonnull Map<String, Object> config) { requireNonBlank(mvcType, ERROR_MVCTYPE_BLANK); requireNonNull(config, ERROR_CONFIG_NULL); MVCGroupConfiguration configuration = findConfiguration(mvcType); Map<String, Object> configCopy = new LinkedHashMap<>(); configCopy.putAll(configuration.getConfig()); configCopy.putAll(config); return newMVCGroupConfiguration(mvcType, configuration.getMembers(), configCopy); } @Nonnull protected List<? extends GriffonMvcArtifact> createMVC(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> args) { MVCGroup group = createMVCGroup(findConfiguration(configuration.getMvcType()), mvcId, args); return asList(group.getModel(), group.getView(), group.getController()); } @SuppressWarnings("unchecked") protected <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCFunction<M, V, C> handler) { MVCGroup group = null; try { group = createMVCGroup(configuration, mvcId, args); handler.apply((M) group.getModel(), (V) group.getView(), (C) group.getController()); } finally { try { if (group != null) { destroyMVCGroup(group.getMvcId()); } } catch (Exception x) { LOG.warn("Could not destroy group [{}] of type {}", mvcId, configuration.getMvcType(), sanitize(x)); } } } @SuppressWarnings("unchecked") protected void withMVCGroup(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) { MVCGroup group = null; try { group = createMVCGroup(configuration, mvcId, args); handler.apply(group); } finally { try { if (group != null) { destroyMVCGroup(group.getMvcId()); } } catch (Exception x) { LOG.warn("Could not destroy group [{}] of type {}", mvcId, configuration.getMvcType(), sanitize(x)); } } } @Nonnull protected abstract MVCGroup createMVCGroup(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> args); protected abstract void doInitialize(@Nonnull Map<String, MVCGroupConfiguration> configurations); @Nonnull @Override public MVCGroup createMVCGroup(@Nonnull String mvcType) { return createMVCGroup(findConfiguration(mvcType), null, Collections.<String, Object>emptyMap()); } @Nonnull @Override public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId) { return createMVCGroup(findConfiguration(mvcType), mvcId, Collections.<String, Object>emptyMap()); } @Nonnull @Override public MVCGroup createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType) { return createMVCGroup(findConfiguration(mvcType), null, args); } @Nonnull @Override public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args) { return createMVCGroup(findConfiguration(mvcType), null, args); } @Nonnull @Override public MVCGroup createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) { return createMVCGroup(findConfiguration(mvcType), mvcId, args); } @Nonnull @Override public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) { return createMVCGroup(findConfiguration(mvcType), mvcId, args); } @Nonnull @Override public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType) { return createMVC(findConfiguration(mvcType), null, Collections.<String, Object>emptyMap()); } @Nonnull @Override public List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType) { return createMVC(findConfiguration(mvcType), null, args); } @Nonnull @Override public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull Map<String, Object> args) { return createMVC(findConfiguration(mvcType), null, args); } @Nonnull @Override public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull String mvcId) { return createMVC(findConfiguration(mvcType), mvcId, Collections.<String, Object>emptyMap()); } @Nonnull @Override public List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) { return createMVC(findConfiguration(mvcType), mvcId, args); } @Nonnull @Override public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) { return createMVC(findConfiguration(mvcType), mvcId, args); } @Override public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull MVCFunction<M, V, C> handler) { withMVCGroup(findConfiguration(mvcType), null, Collections.<String, Object>emptyMap(), handler); } @Override public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCFunction<M, V, C> handler) { withMVCGroup(findConfiguration(mvcType), mvcId, Collections.<String, Object>emptyMap(), handler); } @Override public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCFunction<M, V, C> handler) { withMVCGroup(findConfiguration(mvcType), mvcId, args, handler); } @Override public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCFunction<M, V, C> handler) { withMVCGroup(findConfiguration(mvcType), mvcId, args, handler); } @Override public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull Map<String, Object> args, @Nonnull MVCFunction<M, V, C> handler) { withMVCGroup(findConfiguration(mvcType), null, args, handler); } @Override public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull MVCFunction<M, V, C> handler) { withMVCGroup(findConfiguration(mvcType), null, args, handler); } @Override public void withMVCGroup(@Nonnull String mvcType, @Nonnull MVCGroupFunction handler) { withMVCGroup(findConfiguration(mvcType), null, Collections.<String, Object>emptyMap(), handler); } @Override public void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCGroupFunction handler) { withMVCGroup(findConfiguration(mvcType), mvcId, Collections.<String, Object>emptyMap(), handler); } @Override public void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) { withMVCGroup(findConfiguration(mvcType), mvcId, args, handler); } @Override public void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCGroupFunction handler) { withMVCGroup(findConfiguration(mvcType), mvcId, args, handler); } @Override public void withMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) { withMVCGroup(findConfiguration(mvcType), null, args, handler); } @Override public void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull MVCGroupFunction handler) { withMVCGroup(findConfiguration(mvcType), null, args, handler); } @Nonnull @Override public <C extends GriffonController> C getController(@Nonnull String name, @Nonnull Class<C> type) throws ArtifactNotFoundException { requireNonBlank(name, ERROR_NAME_BLANK); requireNonNull(type, ERROR_TYPE_NULL); GriffonController controller = getControllers().get(name); if (controller != null) { return type.cast(controller); } throw new ArtifactNotFoundException(type, name); } @Nonnull @Override public <M extends GriffonModel> M getModel(@Nonnull String name, @Nonnull Class<M> type) throws ArtifactNotFoundException { requireNonBlank(name, ERROR_NAME_BLANK); requireNonNull(type, ERROR_TYPE_NULL); GriffonModel model = getModels().get(name); if (model != null) { return type.cast(model); } throw new ArtifactNotFoundException(type, name); } @Nonnull @Override public <V extends GriffonView> V getView(@Nonnull String name, @Nonnull Class<V> type) throws ArtifactNotFoundException { requireNonBlank(name, ERROR_NAME_BLANK); requireNonNull(type, ERROR_TYPE_NULL); GriffonView view = getViews().get(name); if (view != null) { return type.cast(view); } throw new ArtifactNotFoundException(type, name); } @Nullable @Override public <C extends GriffonController> C findController(@Nonnull String name, @Nonnull Class<C> type) { try { return getController(name, type); } catch (ArtifactNotFoundException anfe) { return null; } } @Nullable @Override public <M extends GriffonModel> M findModel(@Nonnull String name, @Nonnull Class<M> type) { try { return getModel(name, type); } catch (ArtifactNotFoundException anfe) { return null; } } @Nullable @Override public <V extends GriffonView> V findView(@Nonnull String name, @Nonnull Class<V> type) { try { return getView(name, type); } catch (ArtifactNotFoundException anfe) { return null; } } }