AbstractAddonManager.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.addon;
017 
018 import griffon.core.ApplicationEvent;
019 import griffon.core.GriffonApplication;
020 import griffon.core.addon.AddonManager;
021 import griffon.core.addon.GriffonAddon;
022 import griffon.core.mvc.MVCGroupConfiguration;
023 import org.slf4j.Logger;
024 import org.slf4j.LoggerFactory;
025 
026 import javax.annotation.Nonnull;
027 import javax.annotation.Nullable;
028 import javax.annotation.concurrent.GuardedBy;
029 import javax.inject.Inject;
030 import java.util.Collection;
031 import java.util.Collections;
032 import java.util.LinkedHashMap;
033 import java.util.List;
034 import java.util.Map;
035 
036 import static griffon.util.AnnotationUtils.sortByDependencies;
037 import static griffon.util.CollectionUtils.reverse;
038 import static griffon.util.GriffonNameUtils.getPropertyName;
039 import static griffon.util.GriffonNameUtils.requireNonBlank;
040 import static java.util.Arrays.asList;
041 import static java.util.Objects.requireNonNull;
042 
043 /**
044  * Base implementation of the {@code AddonManager} interface.
045  *
046  @author Andres Almiray
047  @since 2.0.0
048  */
049 public abstract class AbstractAddonManager implements AddonManager {
050     private static final Logger LOG = LoggerFactory.getLogger(AbstractAddonManager.class);
051 
052     private static final String ERROR_NAME_BLANK = "Argument 'name' must not be blank";
053     private final Map<String, GriffonAddon> addons = new LinkedHashMap<>();
054     private final Object lock = new Object[0];
055     @GuardedBy("lock")
056     private boolean initialized;
057 
058     private final GriffonApplication application;
059 
060     @Inject
061     public AbstractAddonManager(@Nonnull GriffonApplication application) {
062         this.application = requireNonNull(application, "Argument 'application' must not be null");
063     }
064 
065     @Nonnull
066     public GriffonApplication getApplication() {
067         return application;
068     }
069 
070     @Nonnull
071     public Map<String, GriffonAddon> getAddons() {
072         return Collections.unmodifiableMap(addons);
073     }
074 
075     @Nullable
076     public GriffonAddon findAddon(@Nonnull String name) {
077         requireNonBlank(name, ERROR_NAME_BLANK);
078         if (name.endsWith(GriffonAddon.SUFFIX)) {
079             name = name.substring(0, name.length() 12);
080         }
081         return addons.get(getPropertyName(name));
082     }
083 
084     public final void initialize() {
085         synchronized (lock) {
086             if (!initialized) {
087                 doInitialize();
088                 initialized = true;
089             }
090         }
091     }
092 
093     protected void doInitialize() {
094         LOG.debug("Loading addons [START]");
095 
096         Map<String, GriffonAddon> addons = preloadAddons();
097         event(ApplicationEvent.LOAD_ADDONS_START);
098 
099         for (Map.Entry<String, GriffonAddon> entry : addons.entrySet()) {
100             String name = entry.getKey();
101             GriffonAddon addon = entry.getValue();
102             LOG.debug("Loading addon {} with class {}", name, addon.getClass().getName());
103             event(ApplicationEvent.LOAD_ADDON_START, asList(getApplication(), name, addon));
104 
105             getApplication().getEventRouter().addEventListener(addon);
106             addMVCGroups(addon);
107             addon.init(getApplication());
108 
109             this.addons.put(name, addon);
110             event(ApplicationEvent.LOAD_ADDON_END, asList(getApplication(), name, addon));
111             LOG.debug("Loaded addon {}", name);
112         }
113 
114         for (GriffonAddon addon : reverse(addons.values())) {
115             getApplication().addShutdownHandler(addon);
116         }
117 
118         LOG.debug("Loading addons [END]");
119         event(ApplicationEvent.LOAD_ADDONS_END);
120     }
121 
122     @Nonnull
123     protected Map<String, GriffonAddon> preloadAddons() {
124         Collection<GriffonAddon> addonInstances = getApplication().getInjector().getInstances(GriffonAddon.class);
125         return sortByDependencies(addonInstances, GriffonAddon.SUFFIX, "addon");
126     }
127 
128     @SuppressWarnings("unchecked")
129     protected void addMVCGroups(@Nonnull GriffonAddon addon) {
130         for (Map.Entry<String, Map<String, Object>> groupEntry : addon.getMvcGroups().entrySet()) {
131             String type = groupEntry.getKey();
132             LOG.debug("Adding MVC group {}", type);
133             Map<String, Object> members = groupEntry.getValue();
134             Map<String, Object> configMap = new LinkedHashMap<>();
135             Map<String, String> membersCopy = new LinkedHashMap<>();
136             for (Map.Entry<String, Object> entry : members.entrySet()) {
137                 String key = String.valueOf(entry.getKey());
138                 if ("config".equals(key&& entry.getValue() instanceof Map) {
139                     configMap = (Map<String, Object>entry.getValue();
140                 else {
141                     membersCopy.put(key, String.valueOf(entry.getValue()));
142                 }
143             }
144             MVCGroupConfiguration configuration = getApplication().getMvcGroupManager().newMVCGroupConfiguration(type, membersCopy, configMap);
145             getApplication().getMvcGroupManager().addConfiguration(configuration);
146         }
147     }
148 
149     @Nonnull
150     protected Map<String, GriffonAddon> getAddonsInternal() {
151         return addons;
152     }
153 
154     protected void event(@Nonnull ApplicationEvent evt) {
155         event(evt, asList(getApplication()));
156     }
157 
158     protected void event(@Nonnull ApplicationEvent evt, @Nonnull List<?> args) {
159         getApplication().getEventRouter().publishEvent(evt.getName(), args);
160     }
161 }