001    package org.apache.archiva.consumers.lucene;
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    
022    import org.apache.archiva.admin.model.RepositoryAdminException;
023    import org.apache.archiva.admin.model.beans.ManagedRepository;
024    import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
025    import org.apache.archiva.common.plexusbridge.MavenIndexerUtils;
026    import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
027    import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
028    import org.apache.archiva.configuration.ArchivaConfiguration;
029    import org.apache.archiva.configuration.ConfigurationNames;
030    import org.apache.archiva.configuration.FileTypes;
031    import org.apache.archiva.consumers.AbstractMonitoredConsumer;
032    import org.apache.archiva.consumers.ConsumerException;
033    import org.apache.archiva.consumers.KnownRepositoryContentConsumer;
034    import org.apache.archiva.redback.components.registry.Registry;
035    import org.apache.archiva.scheduler.ArchivaTaskScheduler;
036    import org.apache.archiva.scheduler.indexing.ArtifactIndexingTask;
037    import org.apache.maven.index.NexusIndexer;
038    import org.apache.maven.index.context.IndexCreator;
039    import org.apache.maven.index.context.IndexingContext;
040    import org.apache.archiva.redback.components.registry.RegistryListener;
041    import org.apache.archiva.redback.components.taskqueue.TaskQueueException;
042    import org.slf4j.Logger;
043    import org.slf4j.LoggerFactory;
044    import org.springframework.context.annotation.Scope;
045    import org.springframework.stereotype.Service;
046    
047    import javax.annotation.PostConstruct;
048    import javax.inject.Inject;
049    import javax.inject.Named;
050    import java.io.File;
051    import java.util.ArrayList;
052    import java.util.Collections;
053    import java.util.Date;
054    import java.util.List;
055    
056    /**
057     * Consumer for indexing the repository to provide search and IDE integration features.
058     */
059    @Service( "knownRepositoryContentConsumer#index-content" )
060    @Scope( "prototype" )
061    public class NexusIndexerConsumer
062        extends AbstractMonitoredConsumer
063        implements KnownRepositoryContentConsumer, RegistryListener
064    {
065        private Logger log = LoggerFactory.getLogger( getClass() );
066    
067        private ArchivaConfiguration configuration;
068    
069        private FileTypes filetypes;
070    
071        private File managedRepository;
072    
073        private ArchivaTaskScheduler<ArtifactIndexingTask> scheduler;
074    
075        private IndexingContext indexingContext;
076    
077        private NexusIndexer nexusIndexer;
078    
079        private List<String> includes = new ArrayList<String>( 0 );
080    
081        private ManagedRepository repository;
082    
083        private List<? extends IndexCreator> allIndexCreators;
084    
085        private ManagedRepositoryAdmin managedRepositoryAdmin;
086    
087        @Inject
088        public NexusIndexerConsumer(
089            @Named( value = "archivaTaskScheduler#indexing" ) ArchivaTaskScheduler<ArtifactIndexingTask> scheduler,
090            @Named( value = "archivaConfiguration" ) ArchivaConfiguration configuration, FileTypes filetypes,
091            PlexusSisuBridge plexusSisuBridge, MavenIndexerUtils mavenIndexerUtils,
092            ManagedRepositoryAdmin managedRepositoryAdmin )
093            throws PlexusSisuBridgeException
094        {
095            this.configuration = configuration;
096            this.filetypes = filetypes;
097            this.scheduler = scheduler;
098            this.nexusIndexer = plexusSisuBridge.lookup( NexusIndexer.class );
099            this.allIndexCreators = mavenIndexerUtils.getAllIndexCreators();
100            this.managedRepositoryAdmin = managedRepositoryAdmin;
101        }
102    
103        public String getDescription()
104        {
105            return "Indexes the repository to provide search and IDE integration features";
106        }
107    
108        public String getId()
109        {
110            return "index-content";
111        }
112    
113        public boolean isPermanent()
114        {
115            return false;
116        }
117    
118        public void beginScan( ManagedRepository repository, Date whenGathered )
119            throws ConsumerException
120        {
121            this.repository = repository;
122            managedRepository = new File( repository.getLocation() );
123    
124            try
125            {
126                log.info( "Creating indexing context for repo : {}", repository.getId() );
127                indexingContext = managedRepositoryAdmin.createIndexContext( repository );
128            }
129            catch ( RepositoryAdminException e )
130            {
131                throw new ConsumerException( e.getMessage(), e );
132            }
133        }
134    
135        public void beginScan( ManagedRepository repository, Date whenGathered, boolean executeOnEntireRepo )
136            throws ConsumerException
137        {
138            if ( executeOnEntireRepo )
139            {
140                beginScan( repository, whenGathered );
141            }
142            else
143            {
144                this.repository = repository;
145                managedRepository = new File( repository.getLocation() );
146            }
147        }
148    
149        public void processFile( String path )
150            throws ConsumerException
151        {
152            File artifactFile = new File( managedRepository, path );
153    
154            ArtifactIndexingTask task =
155                new ArtifactIndexingTask( repository, artifactFile, ArtifactIndexingTask.Action.ADD, getIndexingContext() );
156            try
157            {
158                log.debug( "Queueing indexing task '{}' to add or update the artifact in the index.", task );
159                scheduler.queueTask( task );
160            }
161            catch ( TaskQueueException e )
162            {
163                throw new ConsumerException( e.getMessage(), e );
164            }
165        }
166    
167        public void processFile( String path, boolean executeOnEntireRepo )
168            throws Exception
169        {
170            if ( executeOnEntireRepo )
171            {
172                processFile( path );
173            }
174            else
175            {
176                File artifactFile = new File( managedRepository, path );
177    
178                // specify in indexing task that this is not a repo scan request!
179                ArtifactIndexingTask task =
180                    new ArtifactIndexingTask( repository, artifactFile, ArtifactIndexingTask.Action.ADD,
181                                              getIndexingContext(), false );
182                // only update index we don't need to scan the full repo here
183                task.setOnlyUpdate( true );
184                try
185                {
186                    log.debug( "Queueing indexing task '{}' to add or update the artifact in the index.", task );
187                    scheduler.queueTask( task );
188                }
189                catch ( TaskQueueException e )
190                {
191                    throw new ConsumerException( e.getMessage(), e );
192                }
193            }
194        }
195    
196        public void completeScan()
197        {
198            IndexingContext context = this.indexingContext;
199            if ( context == null )
200            {
201                try
202                {
203                    context = getIndexingContext();
204                }
205                catch ( ConsumerException e )
206                {
207                    log.warn( "failed to get an IndexingContext:{}", e.getMessage() );
208                    return;
209                }
210            }
211            ArtifactIndexingTask task =
212                new ArtifactIndexingTask( repository, null, ArtifactIndexingTask.Action.FINISH, context );
213            try
214            {
215                log.debug( "Queueing indexing task '{}' to finish indexing.", task );
216                scheduler.queueTask( task );
217            }
218            catch ( TaskQueueException e )
219            {
220                log.error( "Error queueing task: " + task + ": " + e.getMessage(), e );
221            }
222        }
223    
224        public void completeScan( boolean executeOnEntireRepo )
225        {
226            if ( executeOnEntireRepo )
227            {
228                completeScan();
229            }
230    
231            // else, do nothing as the context will be closed when indexing task is executed if not a repo scan request!
232        }
233    
234        public List<String> getExcludes()
235        {
236            return Collections.emptyList();
237        }
238    
239        public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
240        {
241            if ( ConfigurationNames.isRepositoryScanning( propertyName ) )
242            {
243                initIncludes();
244            }
245        }
246    
247        public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
248        {
249            /* do nothing */
250        }
251    
252        private void initIncludes()
253        {
254            List<String> indexable = filetypes.getFileTypePatterns( FileTypes.INDEXABLE_CONTENT );
255            List<String> artifacts = filetypes.getFileTypePatterns( FileTypes.ARTIFACTS );
256    
257            includes = new ArrayList<String>( indexable.size() + artifacts.size() );
258    
259            includes.addAll( indexable );
260    
261            includes.addAll( artifacts );
262        }
263    
264        @PostConstruct
265        public void initialize()
266        {
267            configuration.addChangeListener( this );
268    
269            initIncludes();
270        }
271    
272        public List<String> getIncludes()
273        {
274            return includes;
275        }
276    
277    
278        private IndexingContext getIndexingContext()
279            throws ConsumerException
280        {
281    
282            if ( this.indexingContext == null )
283            {
284                try
285                {
286                    indexingContext = managedRepositoryAdmin.createIndexContext( repository );
287                }
288                catch ( RepositoryAdminException e )
289                {
290                    throw new ConsumerException( e.getMessage(), e );
291                }
292            }
293            return indexingContext;
294        }
295    }