AbstractAddonManager.java
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2008-2017 the original author or authors.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package org.codehaus.griffon.runtime.core.addon;
019 
020 import griffon.core.ApplicationEvent;
021 import griffon.core.GriffonApplication;
022 import griffon.core.addon.AddonManager;
023 import griffon.core.addon.GriffonAddon;
024 import griffon.core.mvc.MVCGroupConfiguration;
025 import org.slf4j.Logger;
026 import org.slf4j.LoggerFactory;
027 
028 import javax.annotation.Nonnull;
029 import javax.annotation.Nullable;
030 import javax.annotation.concurrent.GuardedBy;
031 import javax.inject.Inject;
032 import java.util.Collection;
033 import java.util.Collections;
034 import java.util.LinkedHashMap;
035 import java.util.List;
036 import java.util.Map;
037 
038 import static griffon.util.AnnotationUtils.sortByDependencies;
039 import static griffon.util.CollectionUtils.reverse;
040 import static griffon.util.GriffonNameUtils.getPropertyName;
041 import static griffon.util.GriffonNameUtils.requireNonBlank;
042 import static java.util.Arrays.asList;
043 import static java.util.Objects.requireNonNull;
044 
045 /**
046  * Base implementation of the {@code AddonManager} interface.
047  *
048  @author Andres Almiray
049  @since 2.0.0
050  */
051 public abstract class AbstractAddonManager implements AddonManager {
052     private static final Logger LOG = LoggerFactory.getLogger(AbstractAddonManager.class);
053 
054     private static final String ERROR_NAME_BLANK = "Argument 'name' must not be blank";
055     private final Map<String, GriffonAddon> addons = new LinkedHashMap<>();
056     private final Object lock = new Object[0];
057     @GuardedBy("lock")
058     private boolean initialized;
059 
060     private final GriffonApplication application;
061 
062     @Inject
063     public AbstractAddonManager(@Nonnull GriffonApplication application) {
064         this.application = requireNonNull(application, "Argument 'application' must not be null");
065     }
066 
067     @Nonnull
068     public GriffonApplication getApplication() {
069         return application;
070     }
071 
072     @Nonnull
073     public Map<String, GriffonAddon> getAddons() {
074         return Collections.unmodifiableMap(addons);
075     }
076 
077     @Nullable
078     public GriffonAddon findAddon(@Nonnull String name) {
079         requireNonBlank(name, ERROR_NAME_BLANK);
080         if (name.endsWith(GriffonAddon.SUFFIX)) {
081             name = name.substring(0, name.length() 12);
082         }
083         return addons.get(getPropertyName(name));
084     }
085 
086     public final void initialize() {
087         synchronized (lock) {
088             if (!initialized) {
089                 doInitialize();
090                 initialized = true;
091             }
092         }
093     }
094 
095     protected void doInitialize() {
096         LOG.debug("Loading addons [START]");
097 
098         Map<String, GriffonAddon> addons = preloadAddons();
099         event(ApplicationEvent.LOAD_ADDONS_START);
100 
101         for (Map.Entry<String, GriffonAddon> entry : addons.entrySet()) {
102             String name = entry.getKey();
103             GriffonAddon addon = entry.getValue();
104             LOG.debug("Loading addon {} with class {}", name, addon.getClass().getName());
105             event(ApplicationEvent.LOAD_ADDON_START, asList(getApplication(), name, addon));
106 
107             getApplication().getEventRouter().addEventListener(addon);
108             addMVCGroups(addon);
109             addon.init(getApplication());
110 
111             this.addons.put(name, addon);
112             event(ApplicationEvent.LOAD_ADDON_END, asList(getApplication(), name, addon));
113             LOG.debug("Loaded addon {}", name);
114         }
115 
116         for (GriffonAddon addon : reverse(addons.values())) {
117             getApplication().addShutdownHandler(addon);
118         }
119 
120         LOG.debug("Loading addons [END]");
121         event(ApplicationEvent.LOAD_ADDONS_END);
122     }
123 
124     @Nonnull
125     protected Map<String, GriffonAddon> preloadAddons() {
126         Collection<GriffonAddon> addonInstances = getApplication().getInjector().getInstances(GriffonAddon.class);
127         return sortByDependencies(addonInstances, GriffonAddon.SUFFIX, "addon");
128     }
129 
130     @SuppressWarnings("unchecked")
131     protected void addMVCGroups(@Nonnull GriffonAddon addon) {
132         for (Map.Entry<String, Map<String, Object>> groupEntry : addon.getMvcGroups().entrySet()) {
133             String type = groupEntry.getKey();
134             LOG.debug("Adding MVC group {}", type);
135             Map<String, Object> members = groupEntry.getValue();
136             Map<String, Object> configMap = new LinkedHashMap<>();
137             Map<String, String> membersCopy = new LinkedHashMap<>();
138             for (Map.Entry<String, Object> entry : members.entrySet()) {
139                 String key = String.valueOf(entry.getKey());
140                 if ("config".equals(key&& entry.getValue() instanceof Map) {
141                     configMap = (Map<String, Object>entry.getValue();
142                 else {
143                     membersCopy.put(key, String.valueOf(entry.getValue()));
144                 }
145             }
146             MVCGroupConfiguration configuration = getApplication().getMvcGroupManager().newMVCGroupConfiguration(type, membersCopy, configMap);
147             getApplication().getMvcGroupManager().addConfiguration(configuration);
148         }
149     }
150 
151     @Nonnull
152     protected Map<String, GriffonAddon> getAddonsInternal() {
153         return addons;
154     }
155 
156     protected void event(@Nonnull ApplicationEvent evt) {
157         event(evt, asList(getApplication()));
158     }
159 
160     protected void event(@Nonnull ApplicationEvent evt, @Nonnull List<?> args) {
161         getApplication().getEventRouter().publishEvent(evt.getName(), args);
162     }
163 }