/* * Copyright 2008-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.codehaus.griffon.runtime.core.artifact; import griffon.core.artifact.ArtifactHandler; import griffon.core.artifact.ArtifactManager; import griffon.core.artifact.GriffonArtifact; import griffon.core.artifact.GriffonClass; import griffon.core.artifact.GriffonView; import griffon.core.injection.Injector; import griffon.core.threading.UIThreadManager; import griffon.exceptions.ArtifactHandlerNotFoundException; import griffon.exceptions.ArtifactNotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Provider; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import static griffon.util.GriffonNameUtils.requireNonBlank; import static java.util.Arrays.asList; import static java.util.Objects.requireNonNull; /** * Base implementation of the {@code ArtifactManager} interface. * * @author Andres Almiray * @since 2.0.0 */ @SuppressWarnings("rawtypes") public abstract class AbstractArtifactManager implements ArtifactManager { protected static final String ERROR_ARTIFACT_HANDLER_NULL = "Argument 'artifactHandler' must not be null"; private static final String ERROR_NAME_BLANK = "Argument 'name' must not be blank"; private static final String ERROR_TYPE_BLANK = "Argument 'type' must not be blank"; private static final String ERROR_CLASS_NULL = "Argument 'clazz' must not be null"; private static final String ERROR_ARTIFACT_NULL = "Argument 'artifact' must not be null"; private static final String ERROR_FULLY_QUALIFIED_CLASSNAME_BLANK = "Argument 'fqClassName' must not be blank"; private static final Logger LOG = LoggerFactory.getLogger(AbstractArtifactManager.class); private final Map<String, Class<? extends GriffonArtifact>[]> artifacts = new ConcurrentHashMap<>(); private final Map<String, ArtifactHandler> artifactHandlers = new ConcurrentHashMap<>(); private final Object lock = new Object[0]; @Inject private Provider<Injector> injectorProvider; @Inject private UIThreadManager uiThreadManager; @Nonnull protected Map<String, ArtifactHandler> getArtifactHandlers() { return artifactHandlers; } @SuppressWarnings("unchecked") public final void loadArtifactMetadata() { Map<String, List<Class<? extends GriffonArtifact>>> loadedArtifacts = doLoadArtifactMetadata(); synchronized (lock) { for (Map.Entry<String, List<Class<? extends GriffonArtifact>>> artifactsEntry : loadedArtifacts.entrySet()) { String type = artifactsEntry.getKey(); ArtifactHandler handler = artifactHandlers.get(type); if (handler == null) { throw new ArtifactHandlerNotFoundException(type); } List<Class<? extends GriffonArtifact>> list = artifactsEntry.getValue(); artifacts.put(type, list.toArray(new Class[list.size()])); handler.initialize(artifacts.get(type)); } } } @Nonnull @Override public Set<String> getAllTypes() { return Collections.unmodifiableSet(artifactHandlers.keySet()); } @Override @Nonnull @SuppressWarnings("unchecked") public <A extends GriffonArtifact> A newInstance(@Nonnull GriffonClass griffonClass) { try { requireNonNull(griffonClass, "Argument 'griffonClass' must not be null"); } catch (RuntimeException re) { throw new ArtifactNotFoundException(re); } return newInstance((Class<A>) griffonClass.getClazz()); } @Override @Nonnull @SuppressWarnings("unchecked") public <A extends GriffonArtifact> A newInstance(@Nonnull final Class<A> clazz) { if (findGriffonClass(clazz) == null) { throw new ArtifactNotFoundException(clazz); } if (GriffonView.class.isAssignableFrom(clazz)) { return uiThreadManager.runInsideUISync(new Callable<A>() { @Override public A call() throws Exception { return (A) injectorProvider.get().getInstance(clazz); } }); } return (A) injectorProvider.get().getInstance(clazz); } @Nonnull protected abstract Map<String, List<Class<? extends GriffonArtifact>>> doLoadArtifactMetadata(); public void registerArtifactHandler(@Nonnull ArtifactHandler artifactHandler) { requireNonNull(artifactHandler, ERROR_ARTIFACT_HANDLER_NULL); LOG.debug("Registering artifact handler for type '{}': {}", artifactHandler.getType(), artifactHandler); synchronized (lock) { artifactHandlers.put(artifactHandler.getType(), artifactHandler); } } public void unregisterArtifactHandler(@Nonnull ArtifactHandler artifactHandler) { requireNonNull(artifactHandler, ERROR_ARTIFACT_HANDLER_NULL); LOG.debug("Removing artifact handler for type '{}': {}", artifactHandler.getType(), artifactHandler); synchronized (lock) { artifactHandlers.remove(artifactHandler.getType()); } } protected boolean isArtifactTypeSupported(@Nonnull String type) { requireNonBlank(type, ERROR_TYPE_BLANK); return artifactHandlers.get(type) != null; } @Nullable public GriffonClass findGriffonClass(@Nonnull String name, @Nonnull String type) { requireNonBlank(name, ERROR_NAME_BLANK); requireNonBlank(type, ERROR_TYPE_BLANK); LOG.debug("Searching for griffonClass of {}:{}", type, name); synchronized (lock) { ArtifactHandler handler = artifactHandlers.get(type); return handler != null ? handler.findClassFor(name) : null; } } @Nullable @SuppressWarnings({"unchecked", "rawtypes"}) public GriffonClass findGriffonClass(@Nonnull Class<? extends GriffonArtifact> clazz, @Nonnull String type) { requireNonNull(clazz, ERROR_CLASS_NULL); requireNonBlank(type, ERROR_TYPE_BLANK); LOG.debug("Searching for griffonClass of {}:{}", type, clazz.getName()); synchronized (lock) { ArtifactHandler handler = artifactHandlers.get(type); return handler != null ? handler.getClassFor(clazz) : null; } } @Nullable public <A extends GriffonArtifact> GriffonClass findGriffonClass(@Nonnull A artifact) { requireNonNull(artifact, ERROR_ARTIFACT_NULL); synchronized (lock) { return findGriffonClass(artifact.getTypeClass()); } } @Nullable @SuppressWarnings({"unchecked", "rawtypes"}) public GriffonClass findGriffonClass(@Nonnull Class<? extends GriffonArtifact> clazz) { requireNonNull(clazz, ERROR_CLASS_NULL); LOG.debug("Searching for griffonClass of {}", clazz.getName()); synchronized (lock) { for (ArtifactHandler handler : artifactHandlers.values()) { GriffonClass griffonClass = handler.getClassFor(clazz); if (griffonClass != null) return griffonClass; } } return null; } @Nullable public GriffonClass findGriffonClass(@Nonnull String fqClassName) { requireNonBlank(fqClassName, ERROR_FULLY_QUALIFIED_CLASSNAME_BLANK); LOG.debug("Searching for griffonClass of {}", fqClassName); synchronized (lock) { for (ArtifactHandler handler : artifactHandlers.values()) { GriffonClass griffonClass = handler.getClassFor(fqClassName); if (griffonClass != null) return griffonClass; } } return null; } @Nonnull public List<GriffonClass> getClassesOfType(@Nonnull String type) { requireNonBlank(type, ERROR_TYPE_BLANK); synchronized (lock) { if (artifacts.containsKey(type)) { return asList(artifactHandlers.get(type).getClasses()); } } return EMPTY_GRIFFON_CLASS_LIST; } @Nonnull public List<GriffonClass> getAllClasses() { List<GriffonClass> all = new ArrayList<>(); synchronized (lock) { for (ArtifactHandler handler : artifactHandlers.values()) { all.addAll(asList(handler.getClasses())); } } return Collections.unmodifiableList(all); } protected <A extends GriffonArtifact> boolean isClassOfType(@Nonnull String type, @Nonnull Class<A> clazz) { for (Class<? extends GriffonArtifact> klass : artifacts.get(type)) { if (klass.getName().equals(clazz.getName())) { return true; } } return false; } }