/** * OpenSpotLight - Open Source IT Governance Platform * * Copyright (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA * or third-party contributors as indicated by the @author tags or express * copyright attribution statements applied by the authors. All third-party * contributions are distributed under license by CARAVELATECH CONSULTORIA E * TECNOLOGIA EM INFORMATICA LTDA. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA * *********************************************************************** * OpenSpotLight - Plataforma de Governança de TI de Código Aberto * * Direitos Autorais Reservados (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA * EM INFORMATICA LTDA ou como contribuidores terceiros indicados pela etiqueta * @author ou por expressa atribuição de direito autoral declarada e atribuída pelo autor. * Todas as contribuições de terceiros estão distribuídas sob licença da * CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA. * * Este programa é software livre; você pode redistribuí-lo e/ou modificá-lo sob os * termos da Licença Pública Geral Menor do GNU conforme publicada pela Free Software * Foundation. * * Este programa é distribuído na expectativa de que seja útil, porém, SEM NENHUMA * GARANTIA; nem mesmo a garantia implícita de COMERCIABILIDADE OU ADEQUAÇÃO A UMA * FINALIDADE ESPECÍFICA. Consulte a Licença Pública Geral Menor do GNU para mais detalhes. * * Você deve ter recebido uma cópia da Licença Pública Geral Menor do GNU junto com este * programa; se não, escreva para: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.openspotlight.federation.loader; import static org.openspotlight.common.util.Exceptions.logAndReturn; import static org.openspotlight.common.util.PatternMatcher.filterNamesByPattern; import static org.openspotlight.common.util.Strings.removeBegginingFrom; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Future; import org.openspotlight.common.Pair; import org.openspotlight.common.exception.SLRuntimeException; import org.openspotlight.common.util.Exceptions; import org.openspotlight.common.util.PatternMatcher.FilterResult; import org.openspotlight.common.util.SLCollections; import org.openspotlight.common.util.Strings; import org.openspotlight.domain.ArtifactSource; import org.openspotlight.domain.ArtifactSourceMapping; import org.openspotlight.federation.domain.artifact.Artifact; import org.openspotlight.federation.domain.artifact.ChangeType; import org.openspotlight.federation.domain.artifact.PathElement; import org.openspotlight.federation.finder.OriginArtifactLoader; import org.openspotlight.federation.finder.PersistentArtifactManager; import org.openspotlight.federation.finder.PersistentArtifactManagerProvider; import org.openspotlight.task.ExecutorInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class responsible to load artifacts. It has a public method read its javadoc. * * @author feu */ public enum ArtifactLoaderManager { INSTANCE; /** * It groups the name and type just to perform the artifact cleanup (mark as excluded) after the load task creation. * * @author feu */ private static class ArtifactTypeCleanupResources { public final Set<String> names; public final Class<? extends Artifact> type; public ArtifactTypeCleanupResources(final Class<? extends Artifact> type, final Set<String> names) { this.type = type; this.names = names; } } /** * It maps the necessary data to perform the artifact loading. * * @author feu */ private static class ArtifactTypeResources { public final ArtifactSourceMapping acceptedMapping; public final ArtifactTypeCleanupResources cleanupResources; public final OriginArtifactLoader loader; public final String name; public final Class<? extends Artifact> type; public ArtifactTypeResources(final String name, final OriginArtifactLoader loader, final Class<? extends Artifact> type, final ArtifactSourceMapping acceptedMapping, final ArtifactTypeCleanupResources cleanupResources) { this.name = name; this.loader = loader; this.type = type; this.acceptedMapping = acceptedMapping; this.cleanupResources = cleanupResources; } } /** * Static class responsible to find the change type of each artifact, to reload or load its contents if this is necessary, and * also to map its new name, and preserve the old one as another property. * * @author feu */ private static class LoadAndMapTask implements Callable<Void> { final PersistentArtifactManagerProvider provider; final ArtifactTypeResources r; final ArtifactSource source; public LoadAndMapTask(final PersistentArtifactManagerProvider provider, final ArtifactSource source, final ArtifactTypeResources r) { super(); this.provider = provider; this.source = source; this.r = r; } @Override public Void call() throws Exception { try { final PersistentArtifactManager persistentArtifactManager = provider .get(); final Artifact persisted = persistentArtifactManager .getInternalMethods().findByOriginalName(source, r.type, r.name); final Artifact newOne = r.loader.findByPath(r.type, source, r.name, source.getEncodingForFileContent()); if (newOne == null) { throw logAndReturn(new IllegalStateException( "This exclusion should be handled in another task")); } ChangeType change = null; if (persisted == null) { change = ChangeType.INCLUDED; } else { if (!r.loader.getInternalMethods().isMaybeChanged(source, r.name, persisted)) { change = ChangeType.NOT_CHANGED; } else { if (newOne.contentEquals(persisted)) { change = ChangeType.NOT_CHANGED; } else { change = ChangeType.CHANGED; } } } if (change.equals(ChangeType.NOT_CHANGED) && persisted.getChangeType().equals( ChangeType.NOT_CHANGED)) { return null; } newOne.setMappedFrom(r.acceptedMapping.getFrom()); newOne.setMappedTo(r.acceptedMapping.getTo()); newOne.setChangeType(change); newOne.updateOriginalName(source, r.name); mapNewName(r, newOne); persistentArtifactManager.addTransient(newOne); persistentArtifactManager.saveTransientData(); return null; } catch (final Exception e) { Exceptions.catchAndLog(e); return null; } } } /** * Static class responsible to set the new change type as excluded. * * @author feu */ private static class SetChangeTypeAsExcludedTask implements Callable<Void> { private final String name; private final PersistentArtifactManagerProvider provider; private final ArtifactTypeCleanupResources resources; private final ArtifactSource source; public SetChangeTypeAsExcludedTask(final String name, final ArtifactTypeCleanupResources resources, final PersistentArtifactManagerProvider provider, final ArtifactSource source) { super(); this.name = name; this.resources = resources; this.provider = provider; this.source = source; } @Override public Void call() throws Exception { try { final PersistentArtifactManager manager = provider.get(); Artifact loaded = manager.getInternalMethods() .findByOriginalName(source, resources.type, name); if (loaded == null) { loaded = manager.findByPath(resources.type, name); } if (loaded != null) { loaded.setChangeType(ChangeType.EXCLUDED); manager.addTransient(loaded); manager.saveTransientData(); } return null; } catch (final Exception e) { Exceptions.catchAndLog(e); return null; } } } private final Logger logger = LoggerFactory.getLogger(getClass()); /** * Map the old name to a new name * * @param r * @param newOne */ static void mapNewName(final ArtifactTypeResources r, final Artifact newOne) { String currentPathString = newOne.getParent().getCompletePath(); if (!currentPathString.startsWith("/")) { currentPathString = "/" + currentPathString; } String toRemove = r.acceptedMapping.getFrom(); if (!toRemove.startsWith("/")) { toRemove = "/" + toRemove; } if (currentPathString.startsWith(toRemove)) { currentPathString = Strings.removeBegginingFrom(toRemove, currentPathString); } String newPathString = null; if (!currentPathString.startsWith(r.acceptedMapping.getTo())) { newPathString = r.acceptedMapping.getTo() + currentPathString; } else { newPathString = currentPathString; } newOne.setMappedFrom(r.acceptedMapping.getFrom()); newOne.setMappedTo(r.acceptedMapping.getTo()); final PathElement newPath = PathElement .createFromPathString(newPathString); newOne.setParent(newPath); newOne.getArtifactCompleteName(); } /** * Loads in single thread only the data needed to process all artifacts: the names itself * * @param source * @return * @throws Exception */ private Pair<Set<ArtifactTypeResources>, Set<ArtifactTypeCleanupResources>> loadBaseData(final ArtifactSource source, final PersistentArtifactManagerProvider provider, final Iterable<Class<? extends OriginArtifactLoader>> registry) throws Exception { final Set<ArtifactTypeResources> resourcesToLoad = new CopyOnWriteArraySet<ArtifactTypeResources>(); final Set<ArtifactTypeCleanupResources> resourcesToClean = new CopyOnWriteArraySet<ArtifactTypeCleanupResources>(); for (final Class<? extends OriginArtifactLoader> loaderClass: registry) { final OriginArtifactLoader loader = loaderClass.newInstance(); final Set<Class<? extends Artifact>> types = loader.getInternalMethods() .getAvailableTypes(); for (final Class<? extends Artifact> type: types) { if (loader.getInternalMethods().accept(source, type)) { for (final ArtifactSourceMapping mapping: source.getMappings()) { final Set<String> namesFromOrigin = new HashSet<String>( loader.getInternalMethods() .retrieveOriginalNames(type, source, mapping.getFrom())); if (logger.isDebugEnabled()) { logger.debug("for type " + type.getSimpleName() + " on artifact source " + source.getUrl() + " was loaded " + Strings .bigCollectionsToString(namesFromOrigin)); } final FilterResult result = filterNamesByPattern( Strings.rootPath(mapping.getFrom()), namesFromOrigin, mapping.getIncludeds(), mapping.getExcludeds(), false); final HashSet<String> namesToExclude = new HashSet<String>(); final List<String> rawNamesToExclude = SLCollections .iterableToList(provider .get() .getInternalMethods() .retrieveOriginalNames(source, type, mapping.getFrom())); for (final String raw: rawNamesToExclude) { namesToExclude.add(removeBegginingFrom( source.getInitialLookup(), raw)); } namesToExclude.removeAll(namesFromOrigin); final ArtifactTypeCleanupResources cleanupResources = new ArtifactTypeCleanupResources( type, namesToExclude); resourcesToClean.add(cleanupResources); for (final String s: result.getIncludedNames()) { final ArtifactTypeResources resourcesByType = new ArtifactTypeResources( s, loader, type, mapping, cleanupResources); resourcesToLoad.add(resourcesByType); } if (logger.isDebugEnabled()) { logger.debug("for type " + type.getSimpleName() + " on artifact source " + source.getUrl() + " was included " + Strings.bigCollectionsToString(result .getIncludedNames())); logger.debug("for type " + type.getSimpleName() + " on artifact source " + source.getUrl() + " was ignored " + Strings.bigCollectionsToString(result .getIgnoredNames())); logger.debug("for type " + type.getSimpleName() + " on artifact source " + source.getUrl() + " was excluded " + Strings.bigCollectionsToString(result .getExcludedNames())); } } } else { if (logger.isDebugEnabled()) { logger.debug("ignoring " + type.getSimpleName() + " on artifact source " + source.getUrl()); } } } } return Pair.newPair(resourcesToLoad, resourcesToClean); } /** * It will reload all needed artifacts on a multithreaded environment, if this is possible. The {@link OriginArtifactLoader * loaders} passed as argument must have default constructors, since it was projected to receive the necessary state classes * on load methods. It should work on a multi threaded environment. It should be very easy to do, since there's no need to * store any state on this class. * * @param source */ public void refreshResources(final ArtifactSource source, final PersistentArtifactManagerProvider provider, final Iterable<Class<? extends OriginArtifactLoader>> registry) { try { final Pair<Set<ArtifactTypeResources>, Set<ArtifactTypeCleanupResources>> result = loadBaseData(source, provider, registry); final List<Callable<Void>> tasks = new LinkedList<Callable<Void>>(); for (final ArtifactTypeResources r: result.getK1()) { r.cleanupResources.names.remove(r.name); tasks.add(new LoadAndMapTask(provider, source, r)); } for (final ArtifactTypeCleanupResources cleanup: result.getK2()) { for (final String toRemove: cleanup.names) { tasks.add(new SetChangeTypeAsExcludedTask(toRemove, cleanup, provider, source)); } } if (provider.useOnePerThread()) { for (final Callable<Void> c: tasks) { c.call(); } } else { final List<Future<Void>> results = ExecutorInstance.INSTANCE .invokeAll(tasks); for (final Future<Void> f: results) { f.get(); } } } catch (final Exception e) { throw Exceptions.logAndReturnNew(e, SLRuntimeException.class); } } }