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.maven.artifact.Artifact;
029import org.apache.maven.model.Plugin;
030import org.apache.maven.project.MavenProject;
031import org.codehaus.plexus.classworlds.realm.ClassRealm;
032import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
033import org.codehaus.plexus.component.annotations.Component;
034import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
035import org.eclipse.aether.RepositorySystemSession;
036import org.eclipse.aether.graph.DependencyFilter;
037import org.eclipse.aether.repository.LocalRepository;
038import org.eclipse.aether.repository.RemoteRepository;
039import org.eclipse.aether.repository.WorkspaceRepository;
040
041/**
042 * Default PluginCache implementation. Assumes cached data does not change.
043 */
044@Component( role = PluginRealmCache.class )
045public class DefaultPluginRealmCache
046    implements PluginRealmCache, Disposable
047{
048
049    protected static class CacheKey
050        implements Key
051    {
052
053        private final Plugin plugin;
054
055        private final WorkspaceRepository workspace;
056
057        private final LocalRepository localRepo;
058
059        private final List<RemoteRepository> repositories;
060
061        private final ClassLoader parentRealm;
062
063        private final Map<String, ClassLoader> foreignImports;
064
065        private final DependencyFilter filter;
066
067        private final int hashCode;
068
069        public CacheKey( Plugin plugin, ClassLoader parentRealm, Map<String, ClassLoader> foreignImports,
070                         DependencyFilter dependencyFilter, List<RemoteRepository> repositories,
071                         RepositorySystemSession session )
072        {
073            this.plugin = plugin.clone();
074            this.workspace = CacheUtils.getWorkspace( session );
075            this.localRepo = session.getLocalRepository();
076            this.repositories = new ArrayList<RemoteRepository>( repositories.size() );
077            for ( RemoteRepository repository : repositories )
078            {
079                if ( repository.isRepositoryManager() )
080                {
081                    this.repositories.addAll( repository.getMirroredRepositories() );
082                }
083                else
084                {
085                    this.repositories.add( repository );
086                }
087            }
088            this.parentRealm = parentRealm;
089            this.foreignImports =
090                ( foreignImports != null ) ? foreignImports : Collections.<String, ClassLoader> emptyMap();
091            this.filter = dependencyFilter;
092
093            int hash = 17;
094            hash = hash * 31 + CacheUtils.pluginHashCode( plugin );
095            hash = hash * 31 + hash( workspace );
096            hash = hash * 31 + hash( localRepo );
097            hash = hash * 31 + CacheUtils.repositoriesHashCode( repositories );
098            hash = hash * 31 + hash( parentRealm );
099            hash = hash * 31 + this.foreignImports.hashCode();
100            hash = hash * 31 + hash( dependencyFilter );
101            this.hashCode = hash;
102        }
103
104        @Override
105        public String toString()
106        {
107            return plugin.getId();
108        }
109
110        @Override
111        public int hashCode()
112        {
113            return hashCode;
114        }
115
116        private static int hash( Object obj )
117        {
118            return obj != null ? obj.hashCode() : 0;
119        }
120
121        @Override
122        public boolean equals( Object o )
123        {
124            if ( o == this )
125            {
126                return true;
127            }
128
129            if ( !( o instanceof CacheKey ) )
130            {
131                return false;
132            }
133
134            CacheKey that = (CacheKey) o;
135
136            return parentRealm == that.parentRealm && CacheUtils.pluginEquals( plugin, that.plugin )
137                && eq( workspace, that.workspace ) && eq( localRepo, that.localRepo )
138                && CacheUtils.repositoriesEquals( this.repositories, that.repositories ) && eq( filter, that.filter )
139                && eq( foreignImports, that.foreignImports );
140        }
141
142        private static <T> boolean eq( T s1, T s2 )
143        {
144            return s1 != null ? s1.equals( s2 ) : s2 == null;
145        }
146
147    }
148
149    protected final Map<Key, CacheRecord> cache = new ConcurrentHashMap<Key, CacheRecord>();
150
151    public Key createKey( Plugin plugin, ClassLoader parentRealm, Map<String, ClassLoader> foreignImports,
152                          DependencyFilter dependencyFilter, List<RemoteRepository> repositories,
153                          RepositorySystemSession session )
154    {
155        return new CacheKey( plugin, parentRealm, foreignImports, dependencyFilter, repositories, session );
156    }
157
158    public CacheRecord get( Key key )
159    {
160        return cache.get( key );
161    }
162
163    public CacheRecord put( Key key, ClassRealm pluginRealm, List<Artifact> pluginArtifacts )
164    {
165        if ( pluginRealm == null || pluginArtifacts == null )
166        {
167            throw new IllegalArgumentException();
168        }
169
170        if ( cache.containsKey( key ) )
171        {
172            throw new IllegalStateException( "Duplicate plugin realm for plugin " + key );
173        }
174
175        CacheRecord record = new CacheRecord( pluginRealm, pluginArtifacts );
176
177        cache.put( key, record );
178
179        return record;
180    }
181
182    public void flush()
183    {
184        for ( CacheRecord record : cache.values() )
185        {
186            ClassRealm realm = record.realm;
187            try
188            {
189                realm.getWorld().disposeRealm( realm.getId() );
190            }
191            catch ( NoSuchRealmException e )
192            {
193                // ignore
194            }
195        }
196        cache.clear();
197    }
198
199    protected static int pluginHashCode( Plugin plugin )
200    {
201        return CacheUtils.pluginHashCode( plugin );
202    }
203
204    protected static boolean pluginEquals( Plugin a, Plugin b )
205    {
206        return CacheUtils.pluginEquals( a, b );
207    }
208
209    public void register( MavenProject project, Key key, CacheRecord record )
210    {
211        // default cache does not track plugin usage
212    }
213
214    public void dispose()
215    {
216        flush();
217    }
218
219}