AbstractArtifactManager.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.artifact;
019 
020 import griffon.core.artifact.ArtifactHandler;
021 import griffon.core.artifact.ArtifactManager;
022 import griffon.core.artifact.GriffonArtifact;
023 import griffon.core.artifact.GriffonClass;
024 import griffon.core.artifact.GriffonView;
025 import griffon.core.injection.Injector;
026 import griffon.core.threading.UIThreadManager;
027 import griffon.exceptions.ArtifactHandlerNotFoundException;
028 import griffon.exceptions.ArtifactNotFoundException;
029 import org.slf4j.Logger;
030 import org.slf4j.LoggerFactory;
031 
032 import javax.annotation.Nonnull;
033 import javax.annotation.Nullable;
034 import javax.inject.Inject;
035 import javax.inject.Provider;
036 import java.util.ArrayList;
037 import java.util.Collections;
038 import java.util.List;
039 import java.util.Map;
040 import java.util.Set;
041 import java.util.concurrent.Callable;
042 import java.util.concurrent.ConcurrentHashMap;
043 
044 import static griffon.util.GriffonNameUtils.requireNonBlank;
045 import static java.util.Arrays.asList;
046 import static java.util.Objects.requireNonNull;
047 
048 /**
049  * Base implementation of the {@code ArtifactManager} interface.
050  *
051  @author Andres Almiray
052  @since 2.0.0
053  */
054 @SuppressWarnings("rawtypes")
055 public abstract class AbstractArtifactManager implements ArtifactManager {
056     protected static final String ERROR_ARTIFACT_HANDLER_NULL = "Argument 'artifactHandler' must not be null";
057     private static final String ERROR_NAME_BLANK = "Argument 'name' must not be blank";
058     private static final String ERROR_TYPE_BLANK = "Argument 'type' must not be blank";
059     private static final String ERROR_CLASS_NULL = "Argument 'clazz' must not be null";
060     private static final String ERROR_ARTIFACT_NULL = "Argument 'artifact' must not be null";
061     private static final String ERROR_FULLY_QUALIFIED_CLASSNAME_BLANK = "Argument 'fqClassName' must not be blank";
062     private static final Logger LOG = LoggerFactory.getLogger(AbstractArtifactManager.class);
063     private final Map<String, Class<? extends GriffonArtifact>[]> artifacts = new ConcurrentHashMap<>();
064     private final Map<String, ArtifactHandler> artifactHandlers = new ConcurrentHashMap<>();
065     private final Object lock = new Object[0];
066 
067     @Inject
068     private Provider<Injector> injectorProvider;
069 
070     @Inject
071     private UIThreadManager uiThreadManager;
072 
073     @Nonnull
074     protected Map<String, ArtifactHandler> getArtifactHandlers() {
075         return artifactHandlers;
076     }
077 
078     @SuppressWarnings("unchecked")
079     public final void loadArtifactMetadata() {
080         Map<String, List<Class<? extends GriffonArtifact>>> loadedArtifacts = doLoadArtifactMetadata();
081 
082         synchronized (lock) {
083             for (Map.Entry<String, List<Class<? extends GriffonArtifact>>> artifactsEntry : loadedArtifacts.entrySet()) {
084                 String type = artifactsEntry.getKey();
085                 ArtifactHandler handler = artifactHandlers.get(type);
086                 if (handler == null) {
087                     throw new ArtifactHandlerNotFoundException(type);
088                 }
089                 List<Class<? extends GriffonArtifact>> list = artifactsEntry.getValue();
090                 artifacts.put(type, list.toArray(new Class[list.size()]));
091                 handler.initialize(artifacts.get(type));
092             }
093         }
094     }
095 
096     @Nonnull
097     @Override
098     public Set<String> getAllTypes() {
099         return Collections.unmodifiableSet(artifactHandlers.keySet());
100     }
101 
102     @Override
103     @Nonnull
104     @SuppressWarnings("unchecked")
105     public <A extends GriffonArtifact> A newInstance(@Nonnull GriffonClass griffonClass) {
106         try {
107             requireNonNull(griffonClass, "Argument 'griffonClass' must not be null");
108         catch (RuntimeException re) {
109             throw new ArtifactNotFoundException(re);
110         }
111 
112         return newInstance((Class<A>griffonClass.getClazz());
113     }
114 
115     @Override
116     @Nonnull
117     @SuppressWarnings("unchecked")
118     public <A extends GriffonArtifact> A newInstance(@Nonnull final Class<A> clazz) {
119         if (findGriffonClass(clazz== null) {
120             throw new ArtifactNotFoundException(clazz);
121         }
122 
123         if (GriffonView.class.isAssignableFrom(clazz)) {
124             return uiThreadManager.runInsideUISync(new Callable<A>() {
125                 @Override
126                 public A call() throws Exception {
127                     return (AinjectorProvider.get().getInstance(clazz);
128                 }
129             });
130         }
131 
132         return (AinjectorProvider.get().getInstance(clazz);
133     }
134 
135     @Nonnull
136     protected abstract Map<String, List<Class<? extends GriffonArtifact>>> doLoadArtifactMetadata();
137 
138     public void registerArtifactHandler(@Nonnull ArtifactHandler artifactHandler) {
139         requireNonNull(artifactHandler, ERROR_ARTIFACT_HANDLER_NULL);
140         LOG.debug("Registering artifact handler for type '{}': {}", artifactHandler.getType(), artifactHandler);
141         synchronized (lock) {
142             artifactHandlers.put(artifactHandler.getType(), artifactHandler);
143         }
144     }
145 
146     public void unregisterArtifactHandler(@Nonnull ArtifactHandler artifactHandler) {
147         requireNonNull(artifactHandler, ERROR_ARTIFACT_HANDLER_NULL);
148         LOG.debug("Removing artifact handler for type '{}': {}", artifactHandler.getType(), artifactHandler);
149         synchronized (lock) {
150             artifactHandlers.remove(artifactHandler.getType());
151         }
152     }
153 
154     protected boolean isArtifactTypeSupported(@Nonnull String type) {
155         requireNonBlank(type, ERROR_TYPE_BLANK);
156         return artifactHandlers.get(type!= null;
157     }
158 
159     @Nullable
160     public GriffonClass findGriffonClass(@Nonnull String name, @Nonnull String type) {
161         requireNonBlank(name, ERROR_NAME_BLANK);
162         requireNonBlank(type, ERROR_TYPE_BLANK);
163         LOG.debug("Searching for griffonClass of {}:{}", type, name);
164         synchronized (lock) {
165             ArtifactHandler handler = artifactHandlers.get(type);
166             return handler != null ? handler.findClassFor(namenull;
167         }
168     }
169 
170     @Nullable
171     @SuppressWarnings({"unchecked""rawtypes"})
172     public GriffonClass findGriffonClass(@Nonnull Class<? extends GriffonArtifact> clazz, @Nonnull String type) {
173         requireNonNull(clazz, ERROR_CLASS_NULL);
174         requireNonBlank(type, ERROR_TYPE_BLANK);
175         LOG.debug("Searching for griffonClass of {}:{}", type, clazz.getName());
176         synchronized (lock) {
177             ArtifactHandler handler = artifactHandlers.get(type);
178             return handler != null ? handler.getClassFor(clazznull;
179         }
180     }
181 
182     @Nullable
183     public <A extends GriffonArtifact> GriffonClass findGriffonClass(@Nonnull A artifact) {
184         requireNonNull(artifact, ERROR_ARTIFACT_NULL);
185         synchronized (lock) {
186             return findGriffonClass(artifact.getTypeClass());
187         }
188     }
189 
190     @Nullable
191     @SuppressWarnings({"unchecked""rawtypes"})
192     public GriffonClass findGriffonClass(@Nonnull Class<? extends GriffonArtifact> clazz) {
193         requireNonNull(clazz, ERROR_CLASS_NULL);
194         LOG.debug("Searching for griffonClass of {}", clazz.getName());
195         synchronized (lock) {
196             for (ArtifactHandler handler : artifactHandlers.values()) {
197                 GriffonClass griffonClass = handler.getClassFor(clazz);
198                 if (griffonClass != nullreturn griffonClass;
199             }
200         }
201         return null;
202     }
203 
204     @Nullable
205     public GriffonClass findGriffonClass(@Nonnull String fqClassName) {
206         requireNonBlank(fqClassName, ERROR_FULLY_QUALIFIED_CLASSNAME_BLANK);
207         LOG.debug("Searching for griffonClass of {}", fqClassName);
208         synchronized (lock) {
209             for (ArtifactHandler handler : artifactHandlers.values()) {
210                 GriffonClass griffonClass = handler.getClassFor(fqClassName);
211                 if (griffonClass != nullreturn griffonClass;
212             }
213         }
214         return null;
215     }
216 
217     @Nonnull
218     public List<GriffonClass> getClassesOfType(@Nonnull String type) {
219         requireNonBlank(type, ERROR_TYPE_BLANK);
220         synchronized (lock) {
221             if (artifacts.containsKey(type)) {
222                 return asList(artifactHandlers.get(type).getClasses());
223             }
224         }
225         return EMPTY_GRIFFON_CLASS_LIST;
226     }
227 
228     @Nonnull
229     public List<GriffonClass> getAllClasses() {
230         List<GriffonClass> all = new ArrayList<>();
231         synchronized (lock) {
232             for (ArtifactHandler handler : artifactHandlers.values()) {
233                 all.addAll(asList(handler.getClasses()));
234             }
235         }
236         return Collections.unmodifiableList(all);
237     }
238 
239     protected <A extends GriffonArtifact> boolean isClassOfType(@Nonnull String type, @Nonnull Class<A> clazz) {
240         for (Class<? extends GriffonArtifact> klass : artifacts.get(type)) {
241             if (klass.getName().equals(clazz.getName())) {
242                 return true;
243             }
244         }
245         return false;
246     }
247 }