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 }
|