AbstractMVCGroupManager.java
001 /*
002  * Copyright 2008-2016 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.Context;
019 import griffon.core.ContextFactory;
020 import griffon.core.GriffonApplication;
021 import griffon.core.artifact.GriffonController;
022 import griffon.core.artifact.GriffonModel;
023 import griffon.core.artifact.GriffonMvcArtifact;
024 import griffon.core.artifact.GriffonView;
025 import griffon.core.mvc.MVCFunction;
026 import griffon.core.mvc.MVCGroup;
027 import griffon.core.mvc.MVCGroupConfiguration;
028 import griffon.core.mvc.MVCGroupConfigurationFactory;
029 import griffon.core.mvc.MVCGroupFactory;
030 import griffon.core.mvc.MVCGroupFunction;
031 import griffon.core.mvc.MVCGroupManager;
032 import griffon.exceptions.ArtifactNotFoundException;
033 import griffon.exceptions.MVCGroupConfigurationException;
034 import org.slf4j.Logger;
035 import org.slf4j.LoggerFactory;
036 
037 import javax.annotation.Nonnull;
038 import javax.annotation.Nullable;
039 import javax.inject.Inject;
040 import java.util.Collections;
041 import java.util.LinkedHashMap;
042 import java.util.List;
043 import java.util.Map;
044 
045 import static griffon.core.GriffonExceptionHandler.sanitize;
046 import static griffon.util.GriffonNameUtils.isBlank;
047 import static griffon.util.GriffonNameUtils.requireNonBlank;
048 import static java.util.Arrays.asList;
049 import static java.util.Collections.unmodifiableMap;
050 import static java.util.Objects.requireNonNull;
051 
052 /**
053  * Base implementation of the {@code MVCGroupManager} interface.
054  *
055  @author Andres Almiray
056  @since 2.0.0
057  */
058 public abstract class AbstractMVCGroupManager implements MVCGroupManager {
059     protected static final String ERROR_MVCTYPE_BLANK = "Argument 'mvcType' must not be blank";
060     protected static final String ERROR_MVCID_BLANK = "Argument 'mvcId' must not be blank";
061     protected static final String ERROR_CONFIGURATION_NULL = "Argument 'configuration' must not be null";
062     protected static final String ERROR_GROUP_NULL = "Argument 'group' must not be null";
063     protected static final String ERROR_CONFIG_NULL = "Argument 'config' must not be null";
064     protected static final String ERROR_ARGS_NULL = "Argument 'args' must not be null";
065     protected static final String ERROR_NAME_BLANK = "Argument 'name' cannot be blank";
066     protected static final String ERROR_TYPE_NULL = "Argument 'type' cannot be null";
067     private static final Logger LOG = LoggerFactory.getLogger(AbstractMVCGroupManager.class);
068     private final GriffonApplication application;
069 
070     private final Map<String, MVCGroupConfiguration> configurations = new LinkedHashMap<>();
071     private final Map<String, MVCGroup> groups = new LinkedHashMap<>();
072     private final Object lock = new Object[0];
073     private boolean initialized;
074 
075     @Inject
076     private MVCGroupConfigurationFactory mvcGroupConfigurationFactory;
077 
078     @Inject
079     private MVCGroupFactory mvcGroupFactory;
080 
081     @Inject
082     private ContextFactory contextFactory;
083 
084     @Inject
085     public AbstractMVCGroupManager(@Nonnull GriffonApplication application) {
086         this.application = requireNonNull(application, "Argument 'application' must not be null");
087     }
088 
089     @Override
090     public GriffonApplication getApplication() {
091         return application;
092     }
093 
094     @Nonnull
095     public MVCGroupConfiguration newMVCGroupConfiguration(@Nonnull String mvcType, @Nonnull Map<String, String> members, @Nonnull Map<String, Object> config) {
096         return mvcGroupConfigurationFactory.create(mvcType, members, config);
097     }
098 
099     @Nonnull
100     public MVCGroup newMVCGroup(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> members, @Nullable MVCGroup parentGroup) {
101         return mvcGroupFactory.create(configuration, mvcId, members, parentGroup);
102     }
103 
104     @Nonnull
105     @Override
106     public Context newContext(@Nullable MVCGroup parentGroup) {
107         Context parentContext = parentGroup != null ? parentGroup.getContext() : getApplication().getContext();
108         return contextFactory.create(parentContext);
109     }
110 
111     @Nonnull
112     public Map<String, MVCGroupConfiguration> getConfigurations() {
113         synchronized (lock) {
114             return unmodifiableMap(configurations);
115         }
116     }
117 
118     @Nonnull
119     public Map<String, MVCGroup> getGroups() {
120         synchronized (lock) {
121             return unmodifiableMap(groups);
122         }
123     }
124 
125     @Nonnull
126     public MVCGroupConfiguration findConfiguration(@Nonnull String mvcType) {
127         requireNonBlank(mvcType, ERROR_MVCTYPE_BLANK);
128         MVCGroupConfiguration configuration;
129         synchronized (lock) {
130             configuration = configurations.get(mvcType);
131         }
132 
133         if (configuration == null) {
134             throw new MVCGroupConfigurationException("Unknown MVC type '" + mvcType + "'. Known types are " + configurations.keySet(), mvcType);
135         }
136         return configuration;
137     }
138 
139     @Nullable
140     public MVCGroup findGroup(@Nonnull String mvcId) {
141         requireNonBlank(mvcId, ERROR_MVCID_BLANK);
142         synchronized (lock) {
143             LOG.debug("Searching group {}", mvcId);
144             return groups.get(mvcId);
145         }
146     }
147 
148     @Nullable
149     public MVCGroup getAt(@Nonnull String mvcId) {
150         return findGroup(mvcId);
151     }
152 
153     public final void initialize(@Nonnull Map<String, MVCGroupConfiguration> configurations) {
154         requireNonNull(configurations, "Argument 'configurations' must not be null");
155         if (configurations.isEmpty()) return;
156         synchronized (lock) {
157             if (!initialized) {
158                 doInitialize(configurations);
159                 initialized = true;
160             }
161         }
162     }
163 
164     public void addConfiguration(@Nonnull MVCGroupConfiguration configuration) {
165         requireNonNull(configuration, ERROR_CONFIGURATION_NULL);
166         synchronized (lock) {
167             if (initialized && configurations.get(configuration.getMvcType()) != null) {
168                 return;
169             }
170             configurations.put(configuration.getMvcType(), configuration);
171         }
172     }
173 
174     public void removeConfiguration(@Nonnull MVCGroupConfiguration configuration) {
175         requireNonNull(configuration, ERROR_CONFIGURATION_NULL);
176         removeConfiguration(configuration.getMvcType());
177     }
178 
179     public void removeConfiguration(@Nonnull String name) {
180         requireNonBlank(name, "Argument 'name' must not be blank");
181         if (!isBlank(name)) {
182             synchronized (lock) {
183                 configurations.remove(name);
184             }
185         }
186     }
187 
188     protected void addGroup(@Nonnull MVCGroup group) {
189         requireNonNull(group, ERROR_GROUP_NULL);
190         synchronized (lock) {
191             LOG.debug("Adding group {}:{}", group.getMvcId(), group);
192             groups.put(group.getMvcId(), group);
193         }
194     }
195 
196     protected void removeGroup(@Nonnull MVCGroup group) {
197         requireNonNull(group, ERROR_GROUP_NULL);
198         synchronized (lock) {
199             LOG.debug("Removing group {}:{}", group.getMvcId(), group);
200             groups.remove(group.getMvcId());
201         }
202     }
203 
204     @Nonnull
205     public final Map<String, ? extends GriffonModel> getModels() {
206         Map<String, GriffonModel> models = new LinkedHashMap<>();
207         synchronized (lock) {
208             for (MVCGroup group : groups.values()) {
209                 GriffonModel model = group.getModel();
210                 if (model != null) {
211                     models.put(group.getMvcId(), model);
212                 }
213             }
214         }
215         return unmodifiableMap(models);
216     }
217 
218     @Nonnull
219     public final Map<String, ? extends GriffonView> getViews() {
220         Map<String, GriffonView> views = new LinkedHashMap<>();
221         synchronized (lock) {
222             for (MVCGroup group : groups.values()) {
223                 GriffonView view = group.getView();
224                 if (view != null) {
225                     views.put(group.getMvcId(), view);
226                 }
227             }
228         }
229         return unmodifiableMap(views);
230     }
231 
232     @Nonnull
233     public final Map<String, ? extends GriffonController> getControllers() {
234         Map<String, GriffonController> controllers = new LinkedHashMap<>();
235         synchronized (lock) {
236             for (MVCGroup group : groups.values()) {
237                 GriffonController controller = group.getController();
238                 if (controller != null) {
239                     controllers.put(group.getMvcId(), controller);
240                 }
241             }
242         }
243         return unmodifiableMap(controllers);
244     }
245 
246     @Nonnull
247     @Override
248     public MVCGroupConfiguration cloneMVCGroupConfiguration(@Nonnull String mvcType, @Nonnull Map<String, Object> config) {
249         requireNonBlank(mvcType, ERROR_MVCTYPE_BLANK);
250         requireNonNull(config, ERROR_CONFIG_NULL);
251         MVCGroupConfiguration configuration = findConfiguration(mvcType);
252         Map<String, Object> configCopy = new LinkedHashMap<>();
253         configCopy.putAll(configuration.getConfig());
254         configCopy.putAll(config);
255         return newMVCGroupConfiguration(mvcType, configuration.getMembers(), configCopy);
256     }
257 
258     @Nonnull
259     protected List<? extends GriffonMvcArtifact> createMVC(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> args) {
260         MVCGroup group = createMVCGroup(findConfiguration(configuration.getMvcType()), mvcId, args);
261         return asList(group.getModel(), group.getView(), group.getController());
262     }
263 
264     @SuppressWarnings("unchecked")
265     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) {
266         MVCGroup group = null;
267         try {
268             group = createMVCGroup(configuration, mvcId, args);
269             handler.apply((Mgroup.getModel()(Vgroup.getView()(Cgroup.getController());
270         finally {
271             try {
272                 if (group != null) {
273                     destroyMVCGroup(group.getMvcId());
274                 }
275             catch (Exception x) {
276                 LOG.warn("Could not destroy group [{}] of type {}", mvcId, configuration.getMvcType(), sanitize(x));
277             }
278         }
279     }
280 
281     @SuppressWarnings("unchecked")
282     protected void withMVCGroup(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) {
283         MVCGroup group = null;
284         try {
285             group = createMVCGroup(configuration, mvcId, args);
286             handler.apply(group);
287         finally {
288             try {
289                 if (group != null) {
290                     destroyMVCGroup(group.getMvcId());
291                 }
292             catch (Exception x) {
293                 LOG.warn("Could not destroy group [{}] of type {}", mvcId, configuration.getMvcType(), sanitize(x));
294             }
295         }
296     }
297 
298     @Nonnull
299     protected abstract MVCGroup createMVCGroup(@Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> args);
300 
301     protected abstract void doInitialize(@Nonnull Map<String, MVCGroupConfiguration> configurations);
302 
303     @Nonnull
304     @Override
305     public MVCGroup createMVCGroup(@Nonnull String mvcType) {
306         return createMVCGroup(findConfiguration(mvcType), null, Collections.<String, Object>emptyMap());
307     }
308 
309     @Nonnull
310     @Override
311     public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId) {
312         return createMVCGroup(findConfiguration(mvcType), mvcId, Collections.<String, Object>emptyMap());
313     }
314 
315     @Nonnull
316     @Override
317     public MVCGroup createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType) {
318         return createMVCGroup(findConfiguration(mvcType), null, args);
319     }
320 
321     @Nonnull
322     @Override
323     public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args) {
324         return createMVCGroup(findConfiguration(mvcType), null, args);
325     }
326 
327     @Nonnull
328     @Override
329     public MVCGroup createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) {
330         return createMVCGroup(findConfiguration(mvcType), mvcId, args);
331     }
332 
333     @Nonnull
334     @Override
335     public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
336         return createMVCGroup(findConfiguration(mvcType), mvcId, args);
337     }
338 
339     @Nonnull
340     @Override
341     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType) {
342         return createMVC(findConfiguration(mvcType), null, Collections.<String, Object>emptyMap());
343     }
344 
345     @Nonnull
346     @Override
347     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType) {
348         return createMVC(findConfiguration(mvcType), null, args);
349     }
350 
351     @Nonnull
352     @Override
353     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull Map<String, Object> args) {
354         return createMVC(findConfiguration(mvcType), null, args);
355     }
356 
357     @Nonnull
358     @Override
359     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull String mvcId) {
360         return createMVC(findConfiguration(mvcType), mvcId, Collections.<String, Object>emptyMap());
361     }
362 
363     @Nonnull
364     @Override
365     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) {
366         return createMVC(findConfiguration(mvcType), mvcId, args);
367     }
368 
369     @Nonnull
370     @Override
371     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
372         return createMVC(findConfiguration(mvcType), mvcId, args);
373     }
374 
375     @Override
376     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull MVCFunction<M, V, C> handler) {
377         withMVCGroup(findConfiguration(mvcType), null, Collections.<String, Object>emptyMap(), handler);
378     }
379 
380     @Override
381     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCFunction<M, V, C> handler) {
382         withMVCGroup(findConfiguration(mvcType), mvcId, Collections.<String, Object>emptyMap(), handler);
383     }
384 
385     @Override
386     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) {
387         withMVCGroup(findConfiguration(mvcType), mvcId, args, handler);
388     }
389 
390     @Override
391     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) {
392         withMVCGroup(findConfiguration(mvcType), mvcId, args, handler);
393     }
394 
395     @Override
396     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) {
397         withMVCGroup(findConfiguration(mvcType), null, args, handler);
398     }
399 
400     @Override
401     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) {
402         withMVCGroup(findConfiguration(mvcType), null, args, handler);
403     }
404 
405     @Override
406     public void withMVCGroup(@Nonnull String mvcType, @Nonnull MVCGroupFunction handler) {
407         withMVCGroup(findConfiguration(mvcType), null, Collections.<String, Object>emptyMap(), handler);
408     }
409 
410     @Override
411     public void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCGroupFunction handler) {
412         withMVCGroup(findConfiguration(mvcType), mvcId, Collections.<String, Object>emptyMap(), handler);
413     }
414 
415     @Override
416     public void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) {
417         withMVCGroup(findConfiguration(mvcType), mvcId, args, handler);
418     }
419 
420     @Override
421     public void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCGroupFunction handler) {
422         withMVCGroup(findConfiguration(mvcType), mvcId, args, handler);
423     }
424 
425     @Override
426     public void withMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) {
427         withMVCGroup(findConfiguration(mvcType), null, args, handler);
428     }
429 
430     @Override
431     public void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull MVCGroupFunction handler) {
432         withMVCGroup(findConfiguration(mvcType), null, args, handler);
433     }
434 
435     @Nonnull
436     @Override
437     public <C extends GriffonController> C getController(@Nonnull String name, @Nonnull Class<C> typethrows ArtifactNotFoundException {
438         requireNonBlank(name, ERROR_NAME_BLANK);
439         requireNonNull(type, ERROR_TYPE_NULL);
440         GriffonController controller = getControllers().get(name);
441         if (controller != null) {
442             return type.cast(controller);
443         }
444         throw new ArtifactNotFoundException(type, name);
445     }
446 
447     @Nonnull
448     @Override
449     public <M extends GriffonModel> M getModel(@Nonnull String name, @Nonnull Class<M> typethrows ArtifactNotFoundException {
450         requireNonBlank(name, ERROR_NAME_BLANK);
451         requireNonNull(type, ERROR_TYPE_NULL);
452         GriffonModel model = getModels().get(name);
453         if (model != null) {
454             return type.cast(model);
455         }
456         throw new ArtifactNotFoundException(type, name);
457     }
458 
459     @Nonnull
460     @Override
461     public <V extends GriffonView> V getView(@Nonnull String name, @Nonnull Class<V> typethrows ArtifactNotFoundException {
462         requireNonBlank(name, ERROR_NAME_BLANK);
463         requireNonNull(type, ERROR_TYPE_NULL);
464         GriffonView view = getViews().get(name);
465         if (view != null) {
466             return type.cast(view);
467         }
468         throw new ArtifactNotFoundException(type, name);
469     }
470 
471     @Nullable
472     @Override
473     public <C extends GriffonController> C findController(@Nonnull String name, @Nonnull Class<C> type) {
474         try {
475             return getController(name, type);
476         catch (ArtifactNotFoundException anfe) {
477             return null;
478         }
479     }
480 
481     @Nullable
482     @Override
483     public <M extends GriffonModel> M findModel(@Nonnull String name, @Nonnull Class<M> type) {
484         try {
485             return getModel(name, type);
486         catch (ArtifactNotFoundException anfe) {
487             return null;
488         }
489     }
490 
491     @Nullable
492     @Override
493     public <V extends GriffonView> V findView(@Nonnull String name, @Nonnull Class<V> type) {
494         try {
495             return getView(name, type);
496         catch (ArtifactNotFoundException anfe) {
497             return null;
498         }
499     }
500 }