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.io.File;
023import java.util.ArrayList;
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.project.ExtensionDescriptor;
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;
036
037/**
038 * Default extension realm cache implementation. Assumes cached data does not change.
039 */
040@Component( role = ExtensionRealmCache.class )
041public class DefaultExtensionRealmCache
042    implements ExtensionRealmCache, Disposable
043{
044
045    protected static class CacheKey
046        implements Key
047    {
048
049        private final List<File> files;
050
051        private final List<Long> timestamps;
052
053        private final List<Long> sizes;
054
055        private final List<String> ids;
056
057        private final int hashCode;
058
059        public CacheKey( List<Artifact> extensionArtifacts )
060        {
061            this.files = new ArrayList<>( extensionArtifacts.size() );
062            this.timestamps = new ArrayList<>( extensionArtifacts.size() );
063            this.sizes = new ArrayList<>( extensionArtifacts.size() );
064            this.ids = new ArrayList<>( extensionArtifacts.size() );
065
066            for ( Artifact artifact : extensionArtifacts )
067            {
068                File file = artifact.getFile();
069                files.add( file );
070                timestamps.add( ( file != null ) ? Long.valueOf( file.lastModified() ) : Long.valueOf( 0 ) );
071                sizes.add( ( file != null ) ? Long.valueOf( file.length() ) : Long.valueOf( 0 ) );
072                ids.add( artifact.getVersion() );
073            }
074
075            this.hashCode =
076                31 * files.hashCode() + 31 * ids.hashCode() + 31 * timestamps.hashCode() + 31 * sizes.hashCode();
077        }
078
079        @Override
080        public int hashCode()
081        {
082            return hashCode;
083        }
084
085        @Override
086        public boolean equals( Object o )
087        {
088            if ( o == this )
089            {
090                return true;
091            }
092
093            if ( !( o instanceof CacheKey ) )
094            {
095                return false;
096            }
097
098            CacheKey other = (CacheKey) o;
099
100            return ids.equals( other.ids ) && files.equals( other.files ) && timestamps.equals( other.timestamps )
101                && sizes.equals( other.sizes );
102        }
103
104        @Override
105        public String toString()
106        {
107            return files.toString();
108        }
109    }
110
111    protected final Map<Key, CacheRecord> cache = new ConcurrentHashMap<>();
112
113    @Override
114    public Key createKey( List<Artifact> extensionArtifacts )
115    {
116        return new CacheKey( extensionArtifacts );
117    }
118
119    public CacheRecord get( Key key )
120    {
121        return cache.get( key );
122    }
123
124    public CacheRecord put( Key key, ClassRealm extensionRealm, ExtensionDescriptor extensionDescriptor,
125                            List<Artifact> artifacts )
126    {
127        Validate.notNull( extensionRealm, "extensionRealm cannot be null" );
128
129        if ( cache.containsKey( key ) )
130        {
131            throw new IllegalStateException( "Duplicate extension realm for extension " + key );
132        }
133
134        CacheRecord record = new CacheRecord( extensionRealm, extensionDescriptor, artifacts );
135
136        cache.put( key, record );
137
138        return record;
139    }
140
141    public void flush()
142    {
143        for ( CacheRecord record : cache.values() )
144        {
145            ClassRealm realm = record.realm;
146            try
147            {
148                realm.getWorld().disposeRealm( realm.getId() );
149            }
150            catch ( NoSuchRealmException e )
151            {
152                // ignore
153            }
154        }
155        cache.clear();
156    }
157
158    public void register( MavenProject project, Key key, CacheRecord record )
159    {
160        // default cache does not track extension usage
161    }
162
163    public void dispose()
164    {
165        flush();
166    }
167
168}