001package org.apache.maven.plugin;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.ArrayList;
023import java.util.Collections;
024import java.util.List;
025import java.util.Map;
026import java.util.concurrent.ConcurrentHashMap;
027
028import org.apache.commons.lang3.Validate;
029import org.apache.maven.artifact.Artifact;
030import org.apache.maven.model.Plugin;
031import org.apache.maven.project.MavenProject;
032import org.codehaus.plexus.classworlds.realm.ClassRealm;
033import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
034import org.codehaus.plexus.component.annotations.Component;
035import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
036import org.eclipse.aether.RepositorySystemSession;
037import org.eclipse.aether.graph.DependencyFilter;
038import org.eclipse.aether.repository.LocalRepository;
039import org.eclipse.aether.repository.RemoteRepository;
040import org.eclipse.aether.repository.WorkspaceRepository;
041
042/**
043 * Default PluginCache implementation. Assumes cached data does not change.
044 */
045@Component( role = PluginRealmCache.class )
046public class DefaultPluginRealmCache
047    implements PluginRealmCache, Disposable
048{
049
050    protected static class CacheKey
051        implements Key
052    {
053
054        private final Plugin plugin;
055
056        private final WorkspaceRepository workspace;
057
058        private final LocalRepository localRepo;
059
060        private final List<RemoteRepository> repositories;
061
062        private final ClassLoader parentRealm;
063
064        private final Map<String, ClassLoader> foreignImports;
065
066        private final DependencyFilter filter;
067
068        private final int hashCode;
069
070        public CacheKey( Plugin plugin, ClassLoader parentRealm, Map<String, ClassLoader> foreignImports,
071                         DependencyFilter dependencyFilter, List<RemoteRepository> repositories,
072                         RepositorySystemSession session )
073        {
074            this.plugin = plugin.clone();
075            this.workspace = CacheUtils.getWorkspace( session );
076            this.localRepo = session.getLocalRepository();
077            this.repositories = new ArrayList<>( repositories.size() );
078            for ( RemoteRepository repository : repositories )
079            {
080                if ( repository.isRepositoryManager() )
081                {
082                    this.repositories.addAll( repository.getMirroredRepositories() );
083                }
084                else
085                {
086                    this.repositories.add( repository );
087                }
088            }
089            this.parentRealm = parentRealm;
090            this.foreignImports =
091                ( foreignImports != null ) ? foreignImports : Collections.<String, ClassLoader>emptyMap();
092            this.filter = dependencyFilter;
093
094            int hash = 17;
095            hash = hash * 31 + CacheUtils.pluginHashCode( plugin );
096            hash = hash * 31 + hash( workspace );
097            hash = hash * 31 + hash( localRepo );
098            hash = hash * 31 + CacheUtils.repositoriesHashCode( repositories );
099            hash = hash * 31 + hash( parentRealm );
100            hash = hash * 31 + this.foreignImports.hashCode();
101            hash = hash * 31 + hash( dependencyFilter );
102            this.hashCode = hash;
103        }
104
105        @Override
106        public String toString()
107        {
108            return plugin.getId();
109        }
110
111        @Override
112        public int hashCode()
113        {
114            return hashCode;
115        }
116
117        private static int hash( Object obj )
118        {
119            return obj != null ? obj.hashCode() : 0;
120        }
121
122        @Override
123        public boolean equals( Object o )
124        {
125            if ( o == this )
126            {
127                return true;
128            }
129
130            if ( !( o instanceof CacheKey ) )
131            {
132                return false;
133            }
134
135            CacheKey that = (CacheKey) o;
136
137            return parentRealm == that.parentRealm && CacheUtils.pluginEquals( plugin, that.plugin )
138                && eq( workspace, that.workspace ) && eq( localRepo, that.localRepo )
139                && CacheUtils.repositoriesEquals( this.repositories, that.repositories ) && eq( filter, that.filter )
140                && eq( foreignImports, that.foreignImports );
141        }
142
143        private static <T> boolean eq( T s1, T s2 )
144        {
145            return s1 != null ? s1.equals( s2 ) : s2 == null;
146        }
147
148    }
149
150    protected final Map<Key, CacheRecord> cache = new ConcurrentHashMap<>();
151
152    public Key createKey( Plugin plugin, ClassLoader parentRealm, Map<String, ClassLoader> foreignImports,
153                          DependencyFilter dependencyFilter, List<RemoteRepository> repositories,
154                          RepositorySystemSession session )
155    {
156        return new CacheKey( plugin, parentRealm, foreignImports, dependencyFilter, repositories, session );
157    }
158
159    public CacheRecord get( Key key )
160    {
161        return cache.get( key );
162    }
163
164    public CacheRecord put( Key key, ClassRealm pluginRealm, List<Artifact> pluginArtifacts )
165    {
166        Validate.notNull( pluginRealm, "pluginRealm cannot be null" );
167        Validate.notNull( pluginArtifacts, "pluginArtifacts cannot be null" );
168
169        if ( cache.containsKey( key ) )
170        {
171            throw new IllegalStateException( "Duplicate plugin realm for plugin " + key );
172        }
173
174        CacheRecord record = new CacheRecord( pluginRealm, pluginArtifacts );
175
176        cache.put( key, record );
177
178        return record;
179    }
180
181    public void flush()
182    {
183        for ( CacheRecord record : cache.values() )
184        {
185            ClassRealm realm = record.realm;
186            try
187            {
188                realm.getWorld().disposeRealm( realm.getId() );
189            }
190            catch ( NoSuchRealmException e )
191            {
192                // ignore
193            }
194        }
195        cache.clear();
196    }
197
198    protected static int pluginHashCode( Plugin plugin )
199    {
200        return CacheUtils.pluginHashCode( plugin );
201    }
202
203    protected static boolean pluginEquals( Plugin a, Plugin b )
204    {
205        return CacheUtils.pluginEquals( a, b );
206    }
207
208    public void register( MavenProject project, Key key, CacheRecord record )
209    {
210        // default cache does not track plugin usage
211    }
212
213    public void dispose()
214    {
215        flush();
216    }
217
218}