AbstractMVCGroupManager.java
001 /*
002  * Copyright 2008-2014 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package org.codehaus.griffon.runtime.core.mvc;
017 
018 import griffon.core.GriffonApplication;
019 import griffon.core.artifact.GriffonController;
020 import griffon.core.artifact.GriffonModel;
021 import griffon.core.artifact.GriffonMvcArtifact;
022 import griffon.core.artifact.GriffonView;
023 import griffon.core.mvc.MVCCallable;
024 import griffon.core.mvc.MVCGroup;
025 import griffon.core.mvc.MVCGroupConfiguration;
026 import griffon.core.mvc.MVCGroupManager;
027 import griffon.exceptions.MVCGroupConfigurationException;
028 import org.slf4j.Logger;
029 import org.slf4j.LoggerFactory;
030 
031 import javax.annotation.Nonnull;
032 import javax.annotation.Nullable;
033 import javax.inject.Inject;
034 import java.util.Collections;
035 import java.util.LinkedHashMap;
036 import java.util.List;
037 import java.util.Map;
038 
039 import static griffon.core.GriffonExceptionHandler.sanitize;
040 import static griffon.util.GriffonNameUtils.isBlank;
041 import static griffon.util.GriffonNameUtils.requireNonBlank;
042 import static java.util.Arrays.asList;
043 import static java.util.Collections.unmodifiableMap;
044 import static java.util.Objects.requireNonNull;
045 
046 /**
047  * Base implementation of the {@code MVCGroupManager} interface.
048  *
049  @author Andres Almiray
050  @since 2.0.0
051  */
052 public abstract class AbstractMVCGroupManager implements MVCGroupManager {
053     private static final Logger LOG = LoggerFactory.getLogger(AbstractMVCGroupManager.class);
054     protected static final String ERROR_MVCTYPE_BLANK = "Argument 'mvcType' must not be blank";
055     protected static final String ERROR_MVCID_BLANK = "Argument 'mvcId' must not be blank";
056     protected static final String ERROR_CONFIGURATION_NULL = "Argument 'configuration' must not be null";
057     protected static final String ERROR_GROUP_NULL = "Argument 'group' must not be null";
058     protected static final String ERROR_CONFIG_NULL = "Argument 'config' must not be null";
059     protected static final String ERROR_ARGS_NULL = "Argument 'args' must not be null";
060 
061     private final GriffonApplication application;
062 
063     private final Map<String, MVCGroupConfiguration> configurations = new LinkedHashMap<>();
064     private final Map<String, MVCGroup> groups = new LinkedHashMap<>();
065     private final Object lock = new Object[0];
066     private boolean initialized;
067 
068     @Inject
069     public AbstractMVCGroupManager(@Nonnull GriffonApplication application) {
070         this.application = requireNonNull(application, "Argument 'application' must not be null");
071     }
072 
073     @Override
074     public GriffonApplication getApplication() {
075         return application;
076     }
077 
078     @Nonnull
079     public Map<String, MVCGroupConfiguration> getConfigurations() {
080         synchronized (lock) {
081             return unmodifiableMap(configurations);
082         }
083     }
084 
085     @Nonnull
086     public Map<String, MVCGroup> getGroups() {
087         synchronized (lock) {
088             return unmodifiableMap(groups);
089         }
090     }
091 
092     @Nonnull
093     public MVCGroupConfiguration findConfiguration(@Nonnull String mvcType) {
094         requireNonBlank(mvcType, ERROR_MVCTYPE_BLANK);
095         MVCGroupConfiguration configuration;
096         synchronized (lock) {
097             configuration = configurations.get(mvcType);
098         }
099 
100         if (configuration == null) {
101             throw new MVCGroupConfigurationException("Unknown MVC type '" + mvcType + "'. Known types are " + configurations.keySet(), mvcType);
102         }
103         return configuration;
104     }
105 
106     @Nullable
107     public MVCGroup findGroup(@Nonnull String mvcId) {
108         requireNonBlank(mvcId, ERROR_MVCID_BLANK);
109         synchronized (lock) {
110             LOG.debug("Searching group {}", mvcId);
111             return groups.get(mvcId);
112         }
113     }
114 
115     @Nullable
116     public MVCGroup getAt(@Nonnull String mvcId) {
117         return findGroup(mvcId);
118     }
119 
120     public final void initialize(@Nonnull Map<String, MVCGroupConfiguration> configurations) {
121         requireNonNull(configurations, "Argument 'configurations' must not be null");
122         if (configurations.isEmpty()) return;
123         synchronized (lock) {
124             if (!initialized) {
125                 doInitialize(configurations);
126                 initialized = true;
127             }
128         }
129     }
130 
131     public void addConfiguration(@Nonnull MVCGroupConfiguration configuration) {
132         requireNonNull(configuration, ERROR_CONFIGURATION_NULL);
133         synchronized (lock) {
134             if (initialized && configurations.get(configuration.getMvcType()) != null) {
135                 return;
136             }
137             configurations.put(configuration.getMvcType(), configuration);
138         }
139     }
140 
141     public void removeConfiguration(@Nonnull MVCGroupConfiguration configuration) {
142         requireNonNull(configuration, ERROR_CONFIGURATION_NULL);
143         removeConfiguration(configuration.getMvcType());
144     }
145 
146     public void removeConfiguration(@Nonnull String name) {
147         requireNonBlank(name, "Argument 'name' must not be blank");
148         if (!isBlank(name)) {
149             synchronized (lock) {
150                 configurations.remove(name);
151             }
152         }
153     }
154 
155     protected void addGroup(@Nonnull MVCGroup group) {
156         requireNonNull(group, ERROR_GROUP_NULL);
157         synchronized (lock) {
158             LOG.debug("Adding group {}:{}", group.getMvcId(), group);
159             groups.put(group.getMvcId(), group);
160         }
161     }
162 
163     protected void removeGroup(@Nonnull MVCGroup group) {
164         requireNonNull(group, ERROR_GROUP_NULL);
165         synchronized (lock) {
166             LOG.debug("Removing group {}:{}", group.getMvcId(), group);
167             groups.remove(group.getMvcId());
168         }
169     }
170 
171     @Nonnull
172     public final Map<String, ? extends GriffonModel> getModels() {
173         Map<String, GriffonModel> models = new LinkedHashMap<>();
174         synchronized (lock) {
175             for (MVCGroup group : groups.values()) {
176                 GriffonModel model = group.getModel();
177                 if (model != null) {
178                     models.put(group.getMvcId(), model);
179                 }
180             }
181         }
182         return unmodifiableMap(models);
183     }
184 
185     @Nonnull
186     public final Map<String, ? extends GriffonView> getViews() {
187         Map<String, GriffonView> views = new LinkedHashMap<>();
188         synchronized (lock) {
189             for (MVCGroup group : groups.values()) {
190                 GriffonView view = group.getView();
191                 if (view != null) {
192                     views.put(group.getMvcId(), view);
193                 }
194             }
195         }
196         return unmodifiableMap(views);
197     }
198 
199     @Nonnull
200     public final Map<String, ? extends GriffonController> getControllers() {
201         Map<String, GriffonController> controllers = new LinkedHashMap<>();
202         synchronized (lock) {
203             for (MVCGroup group : groups.values()) {
204                 GriffonController controller = group.getController();
205                 if (controller != null) {
206                     controllers.put(group.getMvcId(), controller);
207                 }
208             }
209         }
210         return unmodifiableMap(controllers);
211     }
212 
213     @Nonnull
214     @Override
215     public MVCGroupConfiguration cloneMVCGroupConfiguration(@Nonnull String mvcType, @Nonnull Map<String, Object> config) {
216         requireNonBlank(mvcType, ERROR_MVCTYPE_BLANK);
217         requireNonNull(config, ERROR_CONFIG_NULL);
218         MVCGroupConfiguration configuration = findConfiguration(mvcType);
219         Map<String, Object> configCopy = new LinkedHashMap<>();
220         configCopy.putAll(configuration.getConfig());
221         configCopy.putAll(config);
222         return newMVCGroupConfiguration(mvcType, configuration.getMembers(), configCopy);
223     }
224 
225     @Nonnull
226     protected List<? extends GriffonMvcArtifact> createMVCGroup(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> args) {
227         MVCGroup group = buildMVCGroup(findConfiguration(configuration.getMvcType()), mvcId, args);
228         return asList(group.getModel(), group.getView(), group.getController());
229     }
230 
231     @SuppressWarnings("unchecked")
232     protected <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCCallable<M, V, C> handler) {
233         MVCGroup group = null;
234         try {
235             group = buildMVCGroup(configuration, mvcId, args);
236             handler.call((Mgroup.getModel()(Vgroup.getView()(Cgroup.getController());
237         finally {
238             try {
239                 if (group != null) {
240                     destroyMVCGroup(group.getMvcId());
241                 }
242             catch (Exception x) {
243                 LOG.warn("Could not destroy group [{}] of type {}", mvcId, configuration.getMvcType(), sanitize(x));
244 
245             }
246         }
247     }
248 
249     @Nonnull
250     protected abstract MVCGroup buildMVCGroup(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> args);
251 
252     protected abstract void doInitialize(@Nonnull Map<String, MVCGroupConfiguration> configurations);
253 
254     @Nonnull
255     @Override
256     public MVCGroup buildMVCGroup(@Nonnull String mvcType) {
257         return buildMVCGroup(findConfiguration(mvcType), null, Collections.<String, Object>emptyMap());
258     }
259 
260     @Nonnull
261     @Override
262     public MVCGroup buildMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId) {
263         return buildMVCGroup(findConfiguration(mvcType), mvcId, Collections.<String, Object>emptyMap());
264     }
265 
266     @Nonnull
267     @Override
268     public MVCGroup buildMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType) {
269         return buildMVCGroup(findConfiguration(mvcType), null, args);
270     }
271 
272     @Nonnull
273     @Override
274     public MVCGroup buildMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args) {
275         return buildMVCGroup(findConfiguration(mvcType), null, args);
276     }
277 
278     @Nonnull
279     @Override
280     public MVCGroup buildMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) {
281         return buildMVCGroup(findConfiguration(mvcType), mvcId, args);
282     }
283 
284     @Nonnull
285     @Override
286     public MVCGroup buildMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
287         return buildMVCGroup(findConfiguration(mvcType), mvcId, args);
288     }
289 
290     @Nonnull
291     @Override
292     public List<? extends GriffonMvcArtifact> createMVCGroup(@Nonnull String mvcType) {
293         return createMVCGroup(findConfiguration(mvcType), null, Collections.<String, Object>emptyMap());
294     }
295 
296     @Nonnull
297     @Override
298     public List<? extends GriffonMvcArtifact> createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType) {
299         return createMVCGroup(findConfiguration(mvcType), null, args);
300     }
301 
302     @Nonnull
303     @Override
304     public List<? extends GriffonMvcArtifact> createMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args) {
305         return createMVCGroup(findConfiguration(mvcType), null, args);
306     }
307 
308     @Nonnull
309     @Override
310     public List<? extends GriffonMvcArtifact> createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId) {
311         return createMVCGroup(findConfiguration(mvcType), mvcId, Collections.<String, Object>emptyMap());
312     }
313 
314     @Nonnull
315     @Override
316     public List<? extends GriffonMvcArtifact> createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) {
317         return createMVCGroup(findConfiguration(mvcType), mvcId, args);
318     }
319 
320     @Nonnull
321     @Override
322     public List<? extends GriffonMvcArtifact> createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
323         return createMVCGroup(findConfiguration(mvcType), mvcId, args);
324     }
325 
326     @Override
327     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(@Nonnull String mvcType, @Nonnull MVCCallable<M, V, C> handler) {
328         withMVCGroup(findConfiguration(mvcType), null, Collections.<String, Object>emptyMap(), handler);
329     }
330 
331     @Override
332     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCCallable<M, V, C> handler) {
333         withMVCGroup(findConfiguration(mvcType), mvcId, Collections.<String, Object>emptyMap(), handler);
334     }
335 
336     @Override
337     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCCallable<M, V, C> handler) {
338         withMVCGroup(findConfiguration(mvcType), mvcId, args, handler);
339     }
340 
341     @Override
342     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCCallable<M, V, C> handler) {
343         withMVCGroup(findConfiguration(mvcType), mvcId, args, handler);
344     }
345 
346     @Override
347     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args, @Nonnull MVCCallable<M, V, C> handler) {
348         withMVCGroup(findConfiguration(mvcType), null, args, handler);
349     }
350 
351     @Override
352     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull MVCCallable<M, V, C> handler) {
353         withMVCGroup(findConfiguration(mvcType), null, args, handler);
354     }
355 }