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