All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.commonjava.indy.implrepo.change.ImpliedRepoMaintainer Maven / Gradle / Ivy

There is a newer version: 3.2.3
Show newest version
/**
 * Copyright (C) 2011-2018 Red Hat, Inc. (https://github.com/Commonjava/indy)
 *
 * 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.commonjava.indy.implrepo.change;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.enterprise.event.Observes;
import javax.inject.Inject;

import org.commonjava.indy.change.event.IndyLifecycleEvent;
import org.commonjava.indy.change.event.ArtifactStorePreUpdateEvent;
import org.commonjava.indy.data.IndyDataException;
import org.commonjava.indy.data.StoreDataManager;
import org.commonjava.indy.implrepo.ImpliedReposException;
import org.commonjava.indy.implrepo.conf.ImpliedRepoConfig;
import org.commonjava.indy.implrepo.data.ImpliedRepoMetadataManager;
import org.commonjava.indy.model.core.ArtifactStore;
import org.commonjava.indy.model.core.Group;
import org.commonjava.indy.model.core.StoreKey;
import org.commonjava.atlas.maven.ident.util.JoinString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ImpliedRepoMaintainer
{

    private final Logger logger = LoggerFactory.getLogger( getClass() );

    @Inject
    private StoreDataManager storeManager;

    @Inject
    private ImpliedRepoMetadataManager metadataManager;

    @Inject
    private ImpliedRepoConfig config;

    protected ImpliedRepoMaintainer()
    {
    }

    public ImpliedRepoMaintainer( final StoreDataManager storeManager, final ImpliedRepoMetadataManager metadataManager,
                                  final ImpliedRepoConfig config )
    {
        this.storeManager = storeManager;
        this.metadataManager = metadataManager;
        this.config = config;
    }

    public void scanAtStart( @Observes final IndyLifecycleEvent event )
    {
        if ( event.getType() != IndyLifecycleEvent.Type.started )
        {
            return;
        }

        logger.info( "Scanning for unincorporated repository implications." );
        try
        {
            final Map stores = mapStores( storeManager.getAllArtifactStores() );

            if ( stores != null )
            {
                for ( final ArtifactStore store : stores.values() )
                {
                    processStore( store, stores );
                }
            }
        }
        catch ( final IndyDataException e )
        {
            logger.error( "Failed to retrieve all known stores.", e );
        }
        catch ( Throwable error )
        {
            Logger logger = LoggerFactory.getLogger( getClass() );
            logger.error( String.format( "Implied-repository maintenance failed: %s", error.getMessage() ), error );
        }

    }

    // FIXME: How to tell whether a repo that is implied by other repos was added manually??? 
    // That, vs. just left there after the repo that implied it was removed???
    // We cannot currently remove formerly implied repos because we can't distinguish between the above states.
    public void updateImpliedStores( @Observes final ArtifactStorePreUpdateEvent event )
    {
        if ( !storeManager.isStarted() )
        {
            return;
        }

        if ( !config.isEnabled() )
        {
            logger.debug( "Implied-repository processing is not enabled." );
            return;
        }

        try
        {
            // TODO: Update for changes map.
            final Map currentStores = mapStores( event );

            for ( final ArtifactStore store : event )
            {
                logger.debug( "Processing store: {} for implied repo metadata", store );
                processStore( store, currentStores );
            }
        }
        catch ( Throwable error )
        {
            Logger logger = LoggerFactory.getLogger( getClass() );
            logger.error( String.format( "Implied-repository maintenance failed: %s", error.getMessage() ), error );
        }
    }

    private void processStore( final ArtifactStore store, final Map currentStores )
    {
        final ImpliedRepoMaintJob job = new ImpliedRepoMaintJob( store, currentStores );
        if ( !initJob( job ) )
        {
            return;
        }

        if ( processImpliedRepos( job ) )
        {
            logger.info( "Group: {} updated with {} implied repositories.", job.group.getKey(), job.added.size() );

            // Since we're getting in ahead of persistence, we shouldn't need to store anything in a store that was in the event.
            //                final String message =
            //                    String.format( "On update of group: %s, implied membership was recalculated.\n\nAdded:"
            //                        + "\n  %s\n\nNOTE: This update may have resulted in stores that were previously "
            //                        + "implied by another member persisting as members even after the store that implied "
            //                        + "them was removed.", store.getName(), new JoinString( "\n  ", job.added ) );
            //
            //                final ChangeSummary summary = new ChangeSummary( ChangeSummary.SYSTEM_USER, message );
            //                try
            //                {
            //                    storeManager.storeArtifactStore( job.store, summary );
            //                }
            //                catch ( final IndyDataException e )
            //                {
            //                    logger.error( "Failed to store implied-membership changes to: " + store.getKey(), e );
            //                }
        }
    }

    private Map mapStores( final Iterable stores )
    {
        final Map result = new HashMap<>();
        if ( stores != null )
        {
            for ( final ArtifactStore store : stores )
            {
                result.put( store.getKey(), store );
            }
        }

        return result;
    }

    private boolean processImpliedRepos( final ImpliedRepoMaintJob job )
    {
        final Set processed = new HashSet<>();

        // pre-load all existing reachable members to processed list, to prevent re-adding them via 
        // implications. Reachable means they could be in nested groups.
        for ( final ArtifactStore member : job.reachableMembers )
        {
            processed.add( member.getKey() );
        }

        logger.debug( "Preset processed-implications to reachable members:\n  {}",
                      new JoinString( "\n  ", processed ) );

        int lastLen = 0;
        boolean changed = false;
        job.added = new ArrayList<>();

//        job.group = job.group.copyOf();

        // iterate through group membership looking for implied stores that aren't already members.
        // For each implied store:
        //  1. load the store
        //  2. add the implied store's key to the processed list
        //  3. add the implied store's key to the group's membership
        //  4. add the implied store to the members list
        // As soon as we go an iteration without adding a new member, we've captured everything.
        do
        {
            lastLen = job.members.size();

            for ( final ArtifactStore member : new ArrayList<>( job.members ) )
            {
                logger.debug( "Processing member: {} for implied repos within group: {}", member.getKey(),
                              job.group.getKey() );
                processed.add( member.getKey() );
                List implied;
                try
                {
                    implied = metadataManager.getStoresImpliedBy( member );
                }
                catch ( final ImpliedReposException e )
                {
                    logger.error( "Failed to retrieve implied-store metadata for: " + member.getKey(), e );
                    continue;
                }

                if ( implied == null || implied.isEmpty() )
                {
                    continue;
                }

                implied.removeAll( processed );

                for ( final StoreKey key : implied )
                {
                    logger.debug( "Found implied store: {} not already in group: {}", key, job.group.getKey() );
                    ArtifactStore impliedStore;
                    try
                    {
                        impliedStore = storeManager.getArtifactStore( key );
                    }
                    catch ( final IndyDataException e )
                    {
                        logger.error( "Failed to retrieve store: " + key + " implied by: " + member.getKey(), e );
                        continue;
                    }

                    logger.info( "Adding: {} to group: {} (implied by POMs in: {})", key, job.group.getKey(),
                                 member.getKey() );

                    processed.add( key );
                    job.added.add( key );

                    job.group.addConstituent( key );

                    job.members.add( impliedStore );
                    changed = true;
                }
            }
        }
        while ( job.members.size() > lastLen );

        return changed;
    }

    private boolean initJob( final ImpliedRepoMaintJob job )
    {
        // TODO: Regardless of whether it's a group, look for implied repo metadata. If found, find the
        // groups this store belongs to and update them. If we use a pre/post event pair like with delete,
        // we might be able to fix the repo-removal problem too.
        if ( !( job.store instanceof Group ) )
        {
            logger.debug( "ImpliedRepoMaint: Ignoring non-group: {}", job.store.getKey() );
            return false;
        }

        if ( !config.isEnabledForGroup( job.store.getName() ) )
        {
            logger.debug( "ImpliedRepoMaint: Implied repositories not enabled for group: {}", job.store.getKey() );
            return false;
        }

        logger.debug( "Processing group: {} for stores implied by membership which are not yet in the membership",
                      job.store.getName() );
        job.group = (Group) job.store;

        try
        {
            // getOrderedStoresInGroup(), but we can't use persisted info...
            job.members = loadMemberStores( job.group, job );

            // getOrderedConcreteStores(), but we can't use the persisted info...
            final LinkedHashSet reachable = new LinkedHashSet<>( job.members.size() );
            for ( final ArtifactStore member : job.members )
            {
                if ( member instanceof Group )
                {
                    reachable.addAll( loadMemberStores( (Group) member, job ) );
                }
                else
                {
                    reachable.add( member );
                }
            }

            job.reachableMembers = new ArrayList<>( reachable );

            logger.debug( "For group: {}\n Members: {}\n  Reachable Concrete Members: {}", job.group.getKey(),
                          job.members, job.reachableMembers );
        }
        catch ( final IndyDataException e )
        {
            logger.error( "Failed to retrieve member stores for group: " + job.group.getName(), e );
        }

        if ( job.members == null )
        {
            logger.debug( "ImpliedRepoMaint: Group: {} has no membership", job.store.getKey() );
            return false;
        }

        return true;
    }

    private List loadMemberStores( final Group group, final ImpliedRepoMaintJob job )
        throws IndyDataException
    {
        final List constituents = new ArrayList<>( group.getConstituents() );
        final List members = new ArrayList<>( constituents.size() );

        for ( final StoreKey memberKey : constituents )
        {
            ArtifactStore store = job.currentStores.get( memberKey );
            if ( store == null )
            {
                store = storeManager.getArtifactStore( memberKey );
            }

            if ( store == null )
            {
                logger.warn( "Store not found for key: {} (member of: {})", memberKey, group.getKey() );
                continue;
            }

            members.add( store );
        }

        return members;
    }

    public static final class ImpliedRepoMaintJob
    {
        List added;

        Group group;

        List reachableMembers;

        List members;

        final ArtifactStore store;

        final Map currentStores;

        public ImpliedRepoMaintJob( final ArtifactStore store, final Map currentStores )
        {
            this.store = store;
            this.currentStores = currentStores;
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy