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