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